diff options
| author | Ellie Hermaszewska <ellieh@nvidia.com> | 2024-10-29 14:49:26 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-29 14:49:26 +0800 |
| commit | f65d756bff8d4c5cbc15bd0322a2ae8e6b896a21 (patch) | |
| tree | ea1d61342cd29368e19135000ec2948813096205 /source/slang/slang-ir.cpp | |
| parent | a729c15e9dce9f5116a38afc66329ab2ca4cea54 (diff) | |
format
* format
* Minor test fixes
* enable checking cpp format in ci
Diffstat (limited to 'source/slang/slang-ir.cpp')
| -rw-r--r-- | source/slang/slang-ir.cpp | 14473 |
1 files changed, 6918 insertions, 7555 deletions
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 7d2a85983..49273163e 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -1,1826 +1,1832 @@ // slang-ir.cpp #include "slang-ir.h" -#include "slang-ir-insts.h" -#include "slang-ir-util.h" #include "../core/slang-basic.h" #include "../core/slang-writer.h" - #include "slang-ir-dominators.h" - +#include "slang-ir-insts.h" +#include "slang-ir-util.h" #include "slang-mangle.h" namespace Slang { - struct IRSpecContext; +struct IRSpecContext; - // !!!!!!!!!!!!!!!!!!!!!!!!!!!! DiagnosticSink Impls !!!!!!!!!!!!!!!!!!!!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! DiagnosticSink Impls !!!!!!!!!!!!!!!!!!!!! - SourceLoc const& getDiagnosticPos(IRInst* inst) +SourceLoc const& getDiagnosticPos(IRInst* inst) +{ + while (inst) { - while (inst) - { - if (inst->sourceLoc.isValid()) - return inst->sourceLoc; - inst = inst->parent; - } - static SourceLoc invalid = SourceLoc(); - return invalid; + if (inst->sourceLoc.isValid()) + return inst->sourceLoc; + inst = inst->parent; } + static SourceLoc invalid = SourceLoc(); + return invalid; +} - void printDiagnosticArg(StringBuilder& sb, IRInst* irObject) +void printDiagnosticArg(StringBuilder& sb, IRInst* irObject) +{ + if (!irObject) + return; + if (as<IRType>(irObject)) { - if (!irObject) - return; - if (as<IRType>(irObject)) - { - getTypeNameHint(sb, irObject); - return; - } - if (auto nameHint = irObject->findDecoration<IRNameHintDecoration>()) - { - sb << nameHint->getName(); - return; - } - if (auto linkage = irObject->findDecoration<IRLinkageDecoration>()) - { - sb << linkage->getMangledName(); - return; - } + getTypeNameHint(sb, irObject); + return; + } + if (auto nameHint = irObject->findDecoration<IRNameHintDecoration>()) + { + sb << nameHint->getName(); + return; } + if (auto linkage = irObject->findDecoration<IRLinkageDecoration>()) + { + sb << linkage->getMangledName(); + return; + } +} - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - bool isSimpleDecoration(IROp op) - { - switch (op) +bool isSimpleDecoration(IROp op) +{ + switch (op) + { + case kIROp_EarlyDepthStencilDecoration: + case kIROp_KeepAliveDecoration: + case kIROp_LineAdjInputPrimitiveTypeDecoration: + case kIROp_LineInputPrimitiveTypeDecoration: + case kIROp_NoInlineDecoration: + case kIROp_DerivativeGroupQuadDecoration: + case kIROp_DerivativeGroupLinearDecoration: + case kIROp_PointInputPrimitiveTypeDecoration: + case kIROp_PreciseDecoration: + case kIROp_PublicDecoration: + case kIROp_HLSLExportDecoration: + case kIROp_ReadNoneDecoration: + case kIROp_NoSideEffectDecoration: + case kIROp_ForwardDifferentiableDecoration: + case kIROp_BackwardDifferentiableDecoration: + case kIROp_RequiresNVAPIDecoration: + case kIROp_TriangleAdjInputPrimitiveTypeDecoration: + case kIROp_TriangleInputPrimitiveTypeDecoration: + case kIROp_UnsafeForceInlineEarlyDecoration: + case kIROp_VulkanCallablePayloadDecoration: + case kIROp_VulkanCallablePayloadInDecoration: + case kIROp_VulkanHitAttributesDecoration: + case kIROp_VulkanRayPayloadDecoration: + case kIROp_VulkanRayPayloadInDecoration: + case kIROp_VulkanHitObjectAttributesDecoration: { - case kIROp_EarlyDepthStencilDecoration: - case kIROp_KeepAliveDecoration: - case kIROp_LineAdjInputPrimitiveTypeDecoration: - case kIROp_LineInputPrimitiveTypeDecoration: - case kIROp_NoInlineDecoration: - case kIROp_DerivativeGroupQuadDecoration: - case kIROp_DerivativeGroupLinearDecoration: - case kIROp_PointInputPrimitiveTypeDecoration: - case kIROp_PreciseDecoration: - case kIROp_PublicDecoration: - case kIROp_HLSLExportDecoration: - case kIROp_ReadNoneDecoration: - case kIROp_NoSideEffectDecoration: - case kIROp_ForwardDifferentiableDecoration: - case kIROp_BackwardDifferentiableDecoration: - case kIROp_RequiresNVAPIDecoration: - case kIROp_TriangleAdjInputPrimitiveTypeDecoration: - case kIROp_TriangleInputPrimitiveTypeDecoration: - case kIROp_UnsafeForceInlineEarlyDecoration: - case kIROp_VulkanCallablePayloadDecoration: - case kIROp_VulkanCallablePayloadInDecoration: - case kIROp_VulkanHitAttributesDecoration: - case kIROp_VulkanRayPayloadDecoration: - case kIROp_VulkanRayPayloadInDecoration: - case kIROp_VulkanHitObjectAttributesDecoration: - { - return true; - } - default: break; + return true; } - return false; + default: break; } + return false; +} - - IRInst* cloneGlobalValueWithLinkage( - IRSpecContext* context, - IRInst* originalVal, - IRLinkageDecoration* originalLinkage); - struct IROpMapEntry - { - IROp op; - IROpInfo info; - }; +IRInst* cloneGlobalValueWithLinkage( + IRSpecContext* context, + IRInst* originalVal, + IRLinkageDecoration* originalLinkage); - // TODO: We should ideally be speeding up the name->inst - // mapping by using a dictionary, or even by pre-computing - // a hash table to be stored as a `static const` array. - // - // NOTE! That this array is now constructed in such a way that looking up - // an entry from an op is fast, by keeping blocks of main, and pseudo ops in same order - // as the ops themselves. Care must be taken to keep this constraint. - static const IROpMapEntry kIROps[] = - { - - // Main ops in order -#define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) \ - { kIROp_##ID, { #MNEMONIC, ARG_COUNT, FLAGS, } }, +struct IROpMapEntry +{ + IROp op; + IROpInfo info; +}; + +// TODO: We should ideally be speeding up the name->inst +// mapping by using a dictionary, or even by pre-computing +// a hash table to be stored as a `static const` array. +// +// NOTE! That this array is now constructed in such a way that looking up +// an entry from an op is fast, by keeping blocks of main, and pseudo ops in same order +// as the ops themselves. Care must be taken to keep this constraint. +static const IROpMapEntry kIROps[] = { + +// Main ops in order +#define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) \ + {kIROp_##ID, \ + { \ + #MNEMONIC, \ + ARG_COUNT, \ + FLAGS, \ + }}, #include "slang-ir-inst-defs.h" // Invalid op sentinel value comes after all the valid ones - { kIROp_Invalid,{ "invalid", 0, 0 } }, - }; + {kIROp_Invalid, {"invalid", 0, 0}}, +}; - IROpInfo getIROpInfo(IROp opIn) +IROpInfo getIROpInfo(IROp opIn) +{ + const int op = opIn & kIROpMask_OpMask; + if (op < kIROpCount) { - const int op = opIn & kIROpMask_OpMask; - if (op < kIROpCount) - { - // It's a main op - const auto& entry = kIROps[op]; - SLANG_ASSERT(entry.op == op); - return entry.info; - } - - // Don't know what this is - SLANG_ASSERT(!"Invalid op"); - SLANG_ASSERT(kIROps[kIROpCount].op == kIROp_Invalid); - return kIROps[kIROpCount].info; + // It's a main op + const auto& entry = kIROps[op]; + SLANG_ASSERT(entry.op == op); + return entry.info; } - IROp findIROp(const UnownedStringSlice& name) - { - for (auto ee : kIROps) - { - if (name == ee.info.name) - return ee.op; - } + // Don't know what this is + SLANG_ASSERT(!"Invalid op"); + SLANG_ASSERT(kIROps[kIROpCount].op == kIROp_Invalid); + return kIROps[kIROpCount].info; +} - return IROp(kIROp_Invalid); +IROp findIROp(const UnownedStringSlice& name) +{ + for (auto ee : kIROps) + { + if (name == ee.info.name) + return ee.op; } - + return IROp(kIROp_Invalid); +} - // - void IRUse::debugValidate() - { +// + +void IRUse::debugValidate() +{ #ifdef _DEBUG - auto uv = this->usedValue; - if(!uv) - { - assert(!nextUse); - assert(!prevLink); - return; - } + auto uv = this->usedValue; + if (!uv) + { + assert(!nextUse); + assert(!prevLink); + return; + } - auto pp = &uv->firstUse; - for(auto u = uv->firstUse; u;) - { - assert(u->prevLink == pp); + auto pp = &uv->firstUse; + for (auto u = uv->firstUse; u;) + { + assert(u->prevLink == pp); - pp = &u->nextUse; - u = u->nextUse; - } -#endif + pp = &u->nextUse; + u = u->nextUse; } +#endif +} - void IRUse::init(IRInst* u, IRInst* v) +void IRUse::init(IRInst* u, IRInst* v) +{ + clear(); + user = u; + usedValue = v; + if (v) { - clear(); - user = u; - usedValue = v; - if(v) - { - nextUse = v->firstUse; - prevLink = &v->firstUse; + nextUse = v->firstUse; + prevLink = &v->firstUse; - if(nextUse) - { - nextUse->prevLink = &this->nextUse; - } - - v->firstUse = this; + if (nextUse) + { + nextUse->prevLink = &this->nextUse; } + + v->firstUse = this; + } #ifdef SLANG_ENABLE_FULL_IR_VALIDATION - debugValidate(); + debugValidate(); #endif - } +} - void IRUse::set(IRInst* uv) - { - // Normally we should never be modifying the operand of an hoistable inst. - // They can be modified by `replaceUsesWith`, or to be replaced by a new inst. - SLANG_ASSERT(!getIROpInfo(user->getOp()).isHoistable() || uv == usedValue); - init(user, uv); - } +void IRUse::set(IRInst* uv) +{ + // Normally we should never be modifying the operand of an hoistable inst. + // They can be modified by `replaceUsesWith`, or to be replaced by a new inst. + SLANG_ASSERT(!getIROpInfo(user->getOp()).isHoistable() || uv == usedValue); + init(user, uv); +} - void IRUse::clear() - { - // This `IRUse` is part of the linked list - // of uses for `usedValue`. +void IRUse::clear() +{ + // This `IRUse` is part of the linked list + // of uses for `usedValue`. #ifdef SLANG_ENABLE_FULL_IR_VALIDATION - debugValidate(); + debugValidate(); #endif - if (usedValue) - { + if (usedValue) + { #ifdef SLANG_ENABLE_FULL_IR_VALIDATION - auto uv = usedValue; + auto uv = usedValue; #endif - *prevLink = nextUse; - if(nextUse) - { - nextUse->prevLink = prevLink; - } + *prevLink = nextUse; + if (nextUse) + { + nextUse->prevLink = prevLink; + } - user = nullptr; - usedValue = nullptr; - nextUse = nullptr; - prevLink = nullptr; + user = nullptr; + usedValue = nullptr; + nextUse = nullptr; + prevLink = nullptr; #ifdef SLANG_ENABLE_FULL_IR_VALIDATION - if(uv->firstUse) - uv->firstUse->debugValidate(); + if (uv->firstUse) + uv->firstUse->debugValidate(); #endif - - } } +} - // IRInstListBase +// IRInstListBase - void IRInstListBase::Iterator::operator++() +void IRInstListBase::Iterator::operator++() +{ + if (inst) { - if (inst) - { - inst = inst->next; - } + inst = inst->next; } +} + +IRInstListBase::Iterator IRInstListBase::begin() +{ + return Iterator(first); +} +IRInstListBase::Iterator IRInstListBase::end() +{ + return Iterator(last ? last->next : nullptr); +} - IRInstListBase::Iterator IRInstListBase::begin() { return Iterator(first); } - IRInstListBase::Iterator IRInstListBase::end() { return Iterator(last ? last->next : nullptr); } +// +IRUse* IRInst::getOperands() +{ + // We assume that *all* instructions are laid out + // in memory such that their arguments come right + // after the first `sizeof(IRInst)` bytes. // + // TODO: we probably need to be careful and make + // this more robust. - IRUse* IRInst::getOperands() - { - // We assume that *all* instructions are laid out - // in memory such that their arguments come right - // after the first `sizeof(IRInst)` bytes. - // - // TODO: we probably need to be careful and make - // this more robust. - - return (IRUse*)(this + 1); - } + return (IRUse*)(this + 1); +} - IRDecoration* IRInst::findDecorationImpl(IROp decorationOp) +IRDecoration* IRInst::findDecorationImpl(IROp decorationOp) +{ + for (auto dd : getDecorations()) { - for(auto dd : getDecorations()) - { - if(dd->getOp() == decorationOp) - return dd; - } - return nullptr; + if (dd->getOp() == decorationOp) + return dd; } + return nullptr; +} - IROperandList<IRAttr> IRInst::getAllAttrs() - { - // We assume as an invariant that all attributes appear at the end of the operand - // list, after all the non-attribute operands. - // - // We will therefore define a range that ends at the end of the operand list ... - // - IRUse* end = getOperands() + getOperandCount(); - // - // ... and begins after the last non-attribute operand. - // - IRUse* cursor = getOperands(); - while(cursor != end && !as<IRAttr>(cursor->get())) - cursor++; +IROperandList<IRAttr> IRInst::getAllAttrs() +{ + // We assume as an invariant that all attributes appear at the end of the operand + // list, after all the non-attribute operands. + // + // We will therefore define a range that ends at the end of the operand list ... + // + IRUse* end = getOperands() + getOperandCount(); + // + // ... and begins after the last non-attribute operand. + // + IRUse* cursor = getOperands(); + while (cursor != end && !as<IRAttr>(cursor->get())) + cursor++; - return IROperandList<IRAttr>(cursor, end); - } + return IROperandList<IRAttr>(cursor, end); +} - // IRConstant +// IRConstant - IRIntegerValue getIntVal(IRInst* inst) +IRIntegerValue getIntVal(IRInst* inst) +{ + switch (inst->getOp()) { - switch (inst->getOp()) - { - default: - SLANG_UNEXPECTED("needed a known integer value"); - UNREACHABLE_RETURN(0); + default: SLANG_UNEXPECTED("needed a known integer value"); UNREACHABLE_RETURN(0); - case kIROp_IntLit: - return static_cast<IRConstant*>(inst)->value.intVal; - break; - } + case kIROp_IntLit: return static_cast<IRConstant*>(inst)->value.intVal; break; } +} - // IRCapabilitySet +// IRCapabilitySet - CapabilitySet IRCapabilitySet::getCaps() +CapabilitySet IRCapabilitySet::getCaps() +{ + switch (getOp()) { - switch (getOp()) + case kIROp_CapabilityConjunction: { - case kIROp_CapabilityConjunction: - { - List<CapabilityName> atoms; + List<CapabilityName> atoms; - Index count = (Index)getOperandCount(); - for (Index i = 0; i < count; ++i) - { - auto operand = cast<IRIntLit>(getOperand(i)); - atoms.add(CapabilityName(operand->getValue())); - } - - return CapabilitySet(atoms.getCount(), atoms.getBuffer()); + Index count = (Index)getOperandCount(); + for (Index i = 0; i < count; ++i) + { + auto operand = cast<IRIntLit>(getOperand(i)); + atoms.add(CapabilityName(operand->getValue())); } - break; - case kIROp_CapabilityDisjunction: + + return CapabilitySet(atoms.getCount(), atoms.getBuffer()); + } + break; + case kIROp_CapabilityDisjunction: + { + CapabilitySet result; + Index count = (Index)getOperandCount(); + for (Index i = 0; i < count; ++i) { - CapabilitySet result; - Index count = (Index) getOperandCount(); - for (Index i = 0; i < count; ++i) - { - auto operand = cast<IRCapabilitySet>(getOperand(i)); - result.unionWith(operand->getCaps()); - } - return result; + auto operand = cast<IRCapabilitySet>(getOperand(i)); + result.unionWith(operand->getCaps()); } - break; + return result; } - return CapabilitySet(); + break; } + return CapabilitySet(); +} - // IRParam +// IRParam - IRParam* IRParam::getNextParam() - { - return as<IRParam, IRDynamicCastBehavior::NoUnwrap>(getNextInst()); - } +IRParam* IRParam::getNextParam() +{ + return as<IRParam, IRDynamicCastBehavior::NoUnwrap>(getNextInst()); +} - IRParam* IRParam::getPrevParam() - { - return as<IRParam, IRDynamicCastBehavior::NoUnwrap>(getPrevInst()); - } +IRParam* IRParam::getPrevParam() +{ + return as<IRParam, IRDynamicCastBehavior::NoUnwrap>(getPrevInst()); +} - // IRArrayTypeBase +// IRArrayTypeBase - IRInst* IRArrayTypeBase::getElementCount() - { - if (auto arrayType = as<IRArrayType>(this)) - return arrayType->getOperand(1); +IRInst* IRArrayTypeBase::getElementCount() +{ + if (auto arrayType = as<IRArrayType>(this)) + return arrayType->getOperand(1); - return nullptr; - } + return nullptr; +} - // IRPtrTypeBase +// IRPtrTypeBase - IRType* tryGetPointedToType( - IRBuilder* builder, - IRType* type) +IRType* tryGetPointedToType(IRBuilder* builder, IRType* type) +{ + if (auto rateQualType = as<IRRateQualifiedType>(type)) { - if( auto rateQualType = as<IRRateQualifiedType>(type) ) - { - type = rateQualType->getValueType(); - } + type = rateQualType->getValueType(); + } - // The "true" pointers and the pointer-like core module types are the easy cases. - if( auto ptrType = as<IRPtrTypeBase>(type) ) - { - return ptrType->getValueType(); - } - else if( auto ptrLikeType = as<IRPointerLikeType>(type) ) - { - return ptrLikeType->getElementType(); - } + // The "true" pointers and the pointer-like core module types are the easy cases. + if (auto ptrType = as<IRPtrTypeBase>(type)) + { + return ptrType->getValueType(); + } + else if (auto ptrLikeType = as<IRPointerLikeType>(type)) + { + return ptrLikeType->getElementType(); + } + // + // A more interesting case arises when we have a `BindExistentials<P<T>, ...>` + // where `P<T>` is a pointer(-like) type. + // + else if (auto bindExistentials = as<IRBindExistentialsType>(type)) + { + // We know that `BindExistentials` won't introduce its own + // existential type parameters, nor will any of the pointer(-like) + // type constructors `P`. // - // A more interesting case arises when we have a `BindExistentials<P<T>, ...>` - // where `P<T>` is a pointer(-like) type. + // Thus we know that the type that is pointed to should be + // the same as `BindExistentials<T, ...>`. // - else if( auto bindExistentials = as<IRBindExistentialsType>(type) ) + auto baseType = bindExistentials->getBaseType(); + if (auto baseElementType = tryGetPointedToType(builder, baseType)) { - // We know that `BindExistentials` won't introduce its own - // existential type parameters, nor will any of the pointer(-like) - // type constructors `P`. - // - // Thus we know that the type that is pointed to should be - // the same as `BindExistentials<T, ...>`. - // - auto baseType = bindExistentials->getBaseType(); - if( auto baseElementType = tryGetPointedToType(builder, baseType) ) + UInt existentialArgCount = bindExistentials->getExistentialArgCount(); + List<IRInst*> existentialArgs; + for (UInt ii = 0; ii < existentialArgCount; ++ii) { - UInt existentialArgCount = bindExistentials->getExistentialArgCount(); - List<IRInst*> existentialArgs; - for( UInt ii = 0; ii < existentialArgCount; ++ii ) - { - existentialArgs.add(bindExistentials->getExistentialArg(ii)); - } - return builder->getBindExistentialsType( - baseElementType, - existentialArgCount, - existentialArgs.getBuffer()); + existentialArgs.add(bindExistentials->getExistentialArg(ii)); } + return builder->getBindExistentialsType( + baseElementType, + existentialArgCount, + existentialArgs.getBuffer()); } + } - // TODO: We may need to handle other cases here. + // TODO: We may need to handle other cases here. - return nullptr; - } + return nullptr; +} - // IRBlock +// IRBlock - IRParam* IRBlock::getLastParam() - { - IRParam* param = getFirstParam(); - if (!param) return nullptr; +IRParam* IRBlock::getLastParam() +{ + IRParam* param = getFirstParam(); + if (!param) + return nullptr; - while (auto nextParam = param->getNextParam()) - param = nextParam; + while (auto nextParam = param->getNextParam()) + param = nextParam; - return param; - } + return param; +} - void IRBlock::addParam(IRParam* param) +void IRBlock::addParam(IRParam* param) +{ + // If there are any existing parameters, + // then insert after the last of them. + // + if (auto lastParam = getLastParam()) { - // If there are any existing parameters, - // then insert after the last of them. - // - if (auto lastParam = getLastParam()) - { - if (lastParam->next) - param->insertAfter(lastParam); - else - param->insertAtEnd(this); - } - // - // Otherwise, if there are any existing - // "ordinary" instructions, insert before - // the first of them. - // - else if(auto firstOrdinary = getFirstOrdinaryInst()) - { - param->insertBefore(firstOrdinary); - } - // - // Otherwise the block currently has neither - // parameters nor orindary instructions, - // so we can safely insert at the end of - // the list of (raw) children. - // + if (lastParam->next) + param->insertAfter(lastParam); else - { param->insertAtEnd(this); - } } - - // Similar to addParam, but instead of appending `param` to the end - // of the parameter list, this function inserts `param` before the - // head of the list. - void IRBlock::insertParamAtHead(IRParam* param) + // + // Otherwise, if there are any existing + // "ordinary" instructions, insert before + // the first of them. + // + else if (auto firstOrdinary = getFirstOrdinaryInst()) { - if (auto firstParam = getFirstParam()) - { - param->insertBefore(firstParam); - } - else if (auto firstOrdinary = getFirstOrdinaryInst()) - { - param->insertBefore(firstOrdinary); - } - else - { - param->insertAtEnd(this); - } + param->insertBefore(firstOrdinary); } - - IRInst* IRBlock::getFirstOrdinaryInst() + // + // Otherwise the block currently has neither + // parameters nor orindary instructions, + // so we can safely insert at the end of + // the list of (raw) children. + // + else { - // Find the last parameter (if any) of the block - auto lastParam = getLastParam(); - if (lastParam) - { - // If there is a last parameter, then the - // instructions after it are the ordinary - // instructions. - return lastParam->getNextInst(); - } - else - { - // If there isn't a last parameter, then - // there must not have been *any* parameters, - // and so the first instruction in the block - // is also the first ordinary one. - return getFirstInst(); - } + param->insertAtEnd(this); } +} - IRInst* IRBlock::getLastOrdinaryInst() +// Similar to addParam, but instead of appending `param` to the end +// of the parameter list, this function inserts `param` before the +// head of the list. +void IRBlock::insertParamAtHead(IRParam* param) +{ + if (auto firstParam = getFirstParam()) { - // Under normal circumstances, the last instruction - // in the block is also the last ordinary instruction. - // However, there is the special case of a block with - // only parameters (which might happen as a temporary - // state while we are building IR). - auto inst = getLastInst(); - - // If the last instruction is a parameter, then - // there are no ordinary instructions, so the last - // one is a null pointer. - if (as<IRParam, IRDynamicCastBehavior::NoUnwrap>(inst)) - return nullptr; - - // Otherwise the last instruction is the last "ordinary" - // instruction as well. - return inst; + param->insertBefore(firstParam); } - - - // The predecessors of a block should all show up as users - // of its value, so rather than explicitly store the CFG, - // we will recover it on demand from the use-def information. - // - // Note: we are really iterating over incoming/outgoing *edges* - // for a block, because there might be multiple uses of a block, - // if more than one way of an N-way branch targets the same block. - - // Get the list of successor blocks for an instruction, - // which we expect to be the last instruction in a block. - static IRBlock::SuccessorList getSuccessors(IRInst* terminator) - { - // If the block somehow isn't terminated, then - // there is no way to read its successors, so - // we return an empty list. - if (!terminator || !as<IRTerminatorInst>(terminator)) - return IRBlock::SuccessorList(nullptr, nullptr); - - // Otherwise, based on the opcode of the terminator - // instruction, we will build up our list of uses. - IRUse* begin = nullptr; - IRUse* end = nullptr; - UInt stride = 1; - - auto operands = terminator->getOperands(); - switch (terminator->getOp()) - { - case kIROp_Return: - case kIROp_Unreachable: - case kIROp_MissingReturn: - case kIROp_GenericAsm: - break; - - case kIROp_unconditionalBranch: - case kIROp_loop: - // unconditonalBranch <block> - begin = operands + 0; - end = begin + 1; - break; - - case kIROp_conditionalBranch: - case kIROp_ifElse: - // conditionalBranch <condition> <trueBlock> <falseBlock> - begin = operands + 1; - end = begin + 2; - break; - - case kIROp_Switch: - // switch <val> <break> <default> <caseVal1> <caseBlock1> ... - begin = operands + 2; - - // TODO: this ends up point one *after* the "one after the end" - // location, so we should really change the representation - // so that we don't need to form this pointer... - end = operands + terminator->getOperandCount() + 1; - stride = 2; - break; - case kIROp_TargetSwitch: - begin = operands + 2; - end = operands + terminator->getOperandCount() + 1; - stride = 2; - break; - default: - SLANG_UNEXPECTED("unhandled terminator instruction"); - UNREACHABLE_RETURN(IRBlock::SuccessorList(nullptr, nullptr)); - } - - return IRBlock::SuccessorList(begin, end, stride); - } - - static IRUse* adjustPredecessorUse(IRUse* use) + else if (auto firstOrdinary = getFirstOrdinaryInst()) { - // We will search until we either find a - // suitable use, or run out of uses. - for (;use; use = use->nextUse) - { - // We only want to deal with uses that represent - // a "sucessor" operand to some terminator instruction. - // We will re-use the logic for getting the successor - // list from such an instruction. - - auto successorList = getSuccessors((IRInst*) use->getUser()); - - if(use >= successorList.begin_ - && use < successorList.end_) - { - UInt index = (use - successorList.begin_); - if ((index % successorList.stride) == 0) - { - // This use is in the range of the sucessor list, - // and so it represents a real edge between - // blocks. - return use; - } - } - } - - // If we ran out of uses, then we are at the end - // of the list of incoming edges. - return nullptr; + param->insertBefore(firstOrdinary); } - - IRBlock::PredecessorList IRBlock::getPredecessors() + else { - // We want to iterate over the predecessors of this block. - // First, we resign ourselves to iterating over the - // incoming edges, rather than the blocks themselves. - // This might sound like a trival distinction, but it is - // possible for there to be multiple edges between two - // blocks (as for a `switch` with multiple cases that - // map to the same code). Any client that wants just - // the unique predecessor blocks needs to deal with - // the deduplication themselves. - // - // Next, we note that for any predecessor edge, there will - // be a use of this block in the terminator instruction of - // the predecessor. We basically just want to iterate over - // the users of this block, then, but we need to be careful - // to rule out anything that doesn't actually represent - // an edge. The `adjustPredecessorUse` function will be - // used to search for a use that actually represents an edge. - - return PredecessorList( - adjustPredecessorUse(firstUse)); + param->insertAtEnd(this); } +} - UInt IRBlock::PredecessorList::getCount() +IRInst* IRBlock::getFirstOrdinaryInst() +{ + // Find the last parameter (if any) of the block + auto lastParam = getLastParam(); + if (lastParam) { - UInt count = 0; - for (auto ii : *this) - { - (void)ii; - count++; - } - return count; + // If there is a last parameter, then the + // instructions after it are the ordinary + // instructions. + return lastParam->getNextInst(); } - - bool IRBlock::PredecessorList::isEmpty() + else { - return !(begin() != end()); + // If there isn't a last parameter, then + // there must not have been *any* parameters, + // and so the first instruction in the block + // is also the first ordinary one. + return getFirstInst(); } +} +IRInst* IRBlock::getLastOrdinaryInst() +{ + // Under normal circumstances, the last instruction + // in the block is also the last ordinary instruction. + // However, there is the special case of a block with + // only parameters (which might happen as a temporary + // state while we are building IR). + auto inst = getLastInst(); + + // If the last instruction is a parameter, then + // there are no ordinary instructions, so the last + // one is a null pointer. + if (as<IRParam, IRDynamicCastBehavior::NoUnwrap>(inst)) + return nullptr; - void IRBlock::PredecessorList::Iterator::operator++() - { - if (!use) return; - use = adjustPredecessorUse(use->nextUse); - } + // Otherwise the last instruction is the last "ordinary" + // instruction as well. + return inst; +} - IRBlock* IRBlock::PredecessorList::Iterator::operator*() - { - if (!use) return nullptr; - return (IRBlock*)use->getUser()->parent; - } - IRBlock::SuccessorList IRBlock::getSuccessors() +// The predecessors of a block should all show up as users +// of its value, so rather than explicitly store the CFG, +// we will recover it on demand from the use-def information. +// +// Note: we are really iterating over incoming/outgoing *edges* +// for a block, because there might be multiple uses of a block, +// if more than one way of an N-way branch targets the same block. + +// Get the list of successor blocks for an instruction, +// which we expect to be the last instruction in a block. +static IRBlock::SuccessorList getSuccessors(IRInst* terminator) +{ + // If the block somehow isn't terminated, then + // there is no way to read its successors, so + // we return an empty list. + if (!terminator || !as<IRTerminatorInst>(terminator)) + return IRBlock::SuccessorList(nullptr, nullptr); + + // Otherwise, based on the opcode of the terminator + // instruction, we will build up our list of uses. + IRUse* begin = nullptr; + IRUse* end = nullptr; + UInt stride = 1; + + auto operands = terminator->getOperands(); + switch (terminator->getOp()) + { + case kIROp_Return: + case kIROp_Unreachable: + case kIROp_MissingReturn: + case kIROp_GenericAsm: break; + + case kIROp_unconditionalBranch: + case kIROp_loop: + // unconditonalBranch <block> + begin = operands + 0; + end = begin + 1; + break; + + case kIROp_conditionalBranch: + case kIROp_ifElse: + // conditionalBranch <condition> <trueBlock> <falseBlock> + begin = operands + 1; + end = begin + 2; + break; + + case kIROp_Switch: + // switch <val> <break> <default> <caseVal1> <caseBlock1> ... + begin = operands + 2; + + // TODO: this ends up point one *after* the "one after the end" + // location, so we should really change the representation + // so that we don't need to form this pointer... + end = operands + terminator->getOperandCount() + 1; + stride = 2; + break; + case kIROp_TargetSwitch: + begin = operands + 2; + end = operands + terminator->getOperandCount() + 1; + stride = 2; + break; + default: + SLANG_UNEXPECTED("unhandled terminator instruction"); + UNREACHABLE_RETURN(IRBlock::SuccessorList(nullptr, nullptr)); + } + + return IRBlock::SuccessorList(begin, end, stride); +} + +static IRUse* adjustPredecessorUse(IRUse* use) +{ + // We will search until we either find a + // suitable use, or run out of uses. + for (; use; use = use->nextUse) { - // The successors of a block will all be listed - // as operands of its terminator instruction. - // Depending on the terminator, we might have - // different numbers of operands to deal with. - // - // (We might also have to deal with a "stride" - // in the case where the basic-block operands - // are mixed up with non-block operands) + // We only want to deal with uses that represent + // a "sucessor" operand to some terminator instruction. + // We will re-use the logic for getting the successor + // list from such an instruction. - auto terminator = getLastInst(); - return Slang::getSuccessors(terminator); - } + auto successorList = getSuccessors((IRInst*)use->getUser()); - UInt IRBlock::SuccessorList::getCount() - { - UInt count = 0; - for (auto ii : *this) + if (use >= successorList.begin_ && use < successorList.end_) { - (void)ii; - count++; + UInt index = (use - successorList.begin_); + if ((index % successorList.stride) == 0) + { + // This use is in the range of the sucessor list, + // and so it represents a real edge between + // blocks. + return use; + } } - return count; } - void IRBlock::SuccessorList::Iterator::operator++() - { - use += stride; - } + // If we ran out of uses, then we are at the end + // of the list of incoming edges. + return nullptr; +} - IRBlock* IRBlock::SuccessorList::Iterator::operator*() +IRBlock::PredecessorList IRBlock::getPredecessors() +{ + // We want to iterate over the predecessors of this block. + // First, we resign ourselves to iterating over the + // incoming edges, rather than the blocks themselves. + // This might sound like a trival distinction, but it is + // possible for there to be multiple edges between two + // blocks (as for a `switch` with multiple cases that + // map to the same code). Any client that wants just + // the unique predecessor blocks needs to deal with + // the deduplication themselves. + // + // Next, we note that for any predecessor edge, there will + // be a use of this block in the terminator instruction of + // the predecessor. We basically just want to iterate over + // the users of this block, then, but we need to be careful + // to rule out anything that doesn't actually represent + // an edge. The `adjustPredecessorUse` function will be + // used to search for a use that actually represents an edge. + + return PredecessorList(adjustPredecessorUse(firstUse)); +} + +UInt IRBlock::PredecessorList::getCount() +{ + UInt count = 0; + for (auto ii : *this) { - return (IRBlock*)use->get(); + (void)ii; + count++; } + return count; +} - UInt IRUnconditionalBranch::getArgCount() - { - switch(getOp()) - { - case kIROp_unconditionalBranch: - return getOperandCount() - 1; - - case kIROp_loop: - return getOperandCount() - 3; +bool IRBlock::PredecessorList::isEmpty() +{ + return !(begin() != end()); +} - default: - SLANG_UNEXPECTED("unhandled unconditional branch opcode"); - UNREACHABLE_RETURN(0); - } - } - IRUse* IRUnconditionalBranch::getArgs() - { - switch(getOp()) - { - case kIROp_unconditionalBranch: - return getOperands() + 1; +void IRBlock::PredecessorList::Iterator::operator++() +{ + if (!use) + return; + use = adjustPredecessorUse(use->nextUse); +} - case kIROp_loop: - return getOperands() + 3; +IRBlock* IRBlock::PredecessorList::Iterator::operator*() +{ + if (!use) + return nullptr; + return (IRBlock*)use->getUser()->parent; +} - default: - SLANG_UNEXPECTED("unhandled unconditional branch opcode"); - UNREACHABLE_RETURN(0); - } - } +IRBlock::SuccessorList IRBlock::getSuccessors() +{ + // The successors of a block will all be listed + // as operands of its terminator instruction. + // Depending on the terminator, we might have + // different numbers of operands to deal with. + // + // (We might also have to deal with a "stride" + // in the case where the basic-block operands + // are mixed up with non-block operands) - void IRUnconditionalBranch::removeArgument(UInt index) - { - switch (getOp()) - { - case kIROp_unconditionalBranch: - removeOperand(1 + index); - break; - case kIROp_loop: - removeOperand(3 + index); - break; - default: - SLANG_UNEXPECTED("unhandled unconditional branch opcode"); - } - } + auto terminator = getLastInst(); + return Slang::getSuccessors(terminator); +} - IRInst* IRUnconditionalBranch::getArg(UInt index) +UInt IRBlock::SuccessorList::getCount() +{ + UInt count = 0; + for (auto ii : *this) { - return getArgs()[index].usedValue; + (void)ii; + count++; } + return count; +} - IRParam* IRGlobalValueWithParams::getFirstParam() - { - auto entryBlock = getFirstBlock(); - if(!entryBlock) return nullptr; +void IRBlock::SuccessorList::Iterator::operator++() +{ + use += stride; +} - return entryBlock->getFirstParam(); - } +IRBlock* IRBlock::SuccessorList::Iterator::operator*() +{ + return (IRBlock*)use->get(); +} - IRParam* IRGlobalValueWithParams::getLastParam() +UInt IRUnconditionalBranch::getArgCount() +{ + switch (getOp()) { - auto entryBlock = getFirstBlock(); - if(!entryBlock) return nullptr; + case kIROp_unconditionalBranch: return getOperandCount() - 1; + + case kIROp_loop: return getOperandCount() - 3; - return entryBlock->getLastParam(); + default: SLANG_UNEXPECTED("unhandled unconditional branch opcode"); UNREACHABLE_RETURN(0); } +} - IRInstList<IRParam> IRGlobalValueWithParams::getParams() +IRUse* IRUnconditionalBranch::getArgs() +{ + switch (getOp()) { - auto entryBlock = getFirstBlock(); - if(!entryBlock) return IRInstList<IRParam>(); + case kIROp_unconditionalBranch: return getOperands() + 1; + + case kIROp_loop: return getOperands() + 3; - return entryBlock->getParams(); + default: SLANG_UNEXPECTED("unhandled unconditional branch opcode"); UNREACHABLE_RETURN(0); } +} - IRInst* IRGlobalValueWithParams::getFirstOrdinaryInst() +void IRUnconditionalBranch::removeArgument(UInt index) +{ + switch (getOp()) { - auto firstBlock = getFirstBlock(); - if (!firstBlock) - return nullptr; - return firstBlock->getFirstOrdinaryInst(); + case kIROp_unconditionalBranch: removeOperand(1 + index); break; + case kIROp_loop: removeOperand(3 + index); break; + default: SLANG_UNEXPECTED("unhandled unconditional branch opcode"); } +} - // IRFunc +IRInst* IRUnconditionalBranch::getArg(UInt index) +{ + return getArgs()[index].usedValue; +} - IRType* IRFunc::getResultType() { return getDataType()->getResultType(); } - UInt IRFunc::getParamCount() { return getDataType()->getParamCount(); } - IRType* IRFunc::getParamType(UInt index) { return getDataType()->getParamType(index); } - - void fixUpFuncType(IRFunc* func, IRType* resultType) - { - SLANG_ASSERT(func); +IRParam* IRGlobalValueWithParams::getFirstParam() +{ + auto entryBlock = getFirstBlock(); + if (!entryBlock) + return nullptr; - auto irModule = func->getModule(); - SLANG_ASSERT(irModule); + return entryBlock->getFirstParam(); +} - IRBuilder builder(irModule); - builder.setInsertBefore(func); +IRParam* IRGlobalValueWithParams::getLastParam() +{ + auto entryBlock = getFirstBlock(); + if (!entryBlock) + return nullptr; - List<IRType*> paramTypes; - for(auto param : func->getParams()) - { - paramTypes.add(param->getFullType()); - } + return entryBlock->getLastParam(); +} - auto funcType = builder.getFuncType(paramTypes, resultType); - builder.setDataType(func, funcType); - } +IRInstList<IRParam> IRGlobalValueWithParams::getParams() +{ + auto entryBlock = getFirstBlock(); + if (!entryBlock) + return IRInstList<IRParam>(); - void fixUpFuncType(IRFunc* func) - { - fixUpFuncType(func, func->getResultType()); - } + return entryBlock->getParams(); +} - // +IRInst* IRGlobalValueWithParams::getFirstOrdinaryInst() +{ + auto firstBlock = getFirstBlock(); + if (!firstBlock) + return nullptr; + return firstBlock->getFirstOrdinaryInst(); +} - bool isTerminatorInst(IROp op) - { - switch (op) - { - default: - return false; +// IRFunc - case kIROp_Return: - case kIROp_unconditionalBranch: - case kIROp_conditionalBranch: - case kIROp_loop: - case kIROp_ifElse: - case kIROp_Switch: - case kIROp_Unreachable: - case kIROp_MissingReturn: - return true; - } - } +IRType* IRFunc::getResultType() +{ + return getDataType()->getResultType(); +} +UInt IRFunc::getParamCount() +{ + return getDataType()->getParamCount(); +} +IRType* IRFunc::getParamType(UInt index) +{ + return getDataType()->getParamType(index); +} + +void fixUpFuncType(IRFunc* func, IRType* resultType) +{ + SLANG_ASSERT(func); - bool isTerminatorInst(IRInst* inst) + auto irModule = func->getModule(); + SLANG_ASSERT(irModule); + + IRBuilder builder(irModule); + builder.setInsertBefore(func); + + List<IRType*> paramTypes; + for (auto param : func->getParams()) { - if (!inst) return false; - return isTerminatorInst(inst->getOp()); + paramTypes.add(param->getFullType()); } - // - // IRTypeLayout - // + auto funcType = builder.getFuncType(paramTypes, resultType); + builder.setDataType(func, funcType); +} - IRTypeSizeAttr* IRTypeLayout::findSizeAttr(LayoutResourceKind kind) - { - // TODO: If we could assume the attributes were sorted - // by `kind`, then we could use a binary search here - // instead of linear. - // - // In practice, the number of entries will be very small, - // so the cost of the linear search should not be too bad. +void fixUpFuncType(IRFunc* func) +{ + fixUpFuncType(func, func->getResultType()); +} - for( auto sizeAttr : getSizeAttrs() ) - { - if(sizeAttr->getResourceKind() == kind) - return sizeAttr; - } - return nullptr; - } +// - IRTypeLayout* IRTypeLayout::unwrapArray() +bool isTerminatorInst(IROp op) +{ + switch (op) { - auto typeLayout = this; - while(auto arrayTypeLayout = as<IRArrayTypeLayout>(typeLayout)) - typeLayout = arrayTypeLayout->getElementTypeLayout(); - return typeLayout; - } + default: return false; - IRTypeLayout* IRTypeLayout::getPendingDataTypeLayout() - { - if(auto attr = findAttr<IRPendingLayoutAttr>()) - return cast<IRTypeLayout>(attr->getLayout()); - return nullptr; + case kIROp_Return: + case kIROp_unconditionalBranch: + case kIROp_conditionalBranch: + case kIROp_loop: + case kIROp_ifElse: + case kIROp_Switch: + case kIROp_Unreachable: + case kIROp_MissingReturn: return true; } +} - IROperandList<IRTypeSizeAttr> IRTypeLayout::getSizeAttrs() - { - return findAttrs<IRTypeSizeAttr>(); - } +bool isTerminatorInst(IRInst* inst) +{ + if (!inst) + return false; + return isTerminatorInst(inst->getOp()); +} - IRTypeLayout::Builder::Builder(IRBuilder* irBuilder) - : m_irBuilder(irBuilder) - {} +// +// IRTypeLayout +// - void IRTypeLayout::Builder::addResourceUsage( - LayoutResourceKind kind, - LayoutSize size) - { - auto& resInfo = m_resInfos[Int(kind)]; - resInfo.kind = kind; - resInfo.size += size; - } +IRTypeSizeAttr* IRTypeLayout::findSizeAttr(LayoutResourceKind kind) +{ + // TODO: If we could assume the attributes were sorted + // by `kind`, then we could use a binary search here + // instead of linear. + // + // In practice, the number of entries will be very small, + // so the cost of the linear search should not be too bad. - void IRTypeLayout::Builder::addResourceUsage(IRTypeSizeAttr* sizeAttr) + for (auto sizeAttr : getSizeAttrs()) { - addResourceUsage( - sizeAttr->getResourceKind(), - sizeAttr->getSize()); + if (sizeAttr->getResourceKind() == kind) + return sizeAttr; } + return nullptr; +} - void IRTypeLayout::Builder::addResourceUsageFrom(IRTypeLayout* typeLayout) - { - for( auto sizeAttr : typeLayout->getSizeAttrs() ) - { - addResourceUsage(sizeAttr); - } - } +IRTypeLayout* IRTypeLayout::unwrapArray() +{ + auto typeLayout = this; + while (auto arrayTypeLayout = as<IRArrayTypeLayout>(typeLayout)) + typeLayout = arrayTypeLayout->getElementTypeLayout(); + return typeLayout; +} - IRTypeLayout* IRTypeLayout::Builder::build() - { - IRBuilder* irBuilder = getIRBuilder(); +IRTypeLayout* IRTypeLayout::getPendingDataTypeLayout() +{ + if (auto attr = findAttr<IRPendingLayoutAttr>()) + return cast<IRTypeLayout>(attr->getLayout()); + return nullptr; +} - List<IRInst*> operands; +IROperandList<IRTypeSizeAttr> IRTypeLayout::getSizeAttrs() +{ + return findAttrs<IRTypeSizeAttr>(); +} - addOperands(operands); - addAttrs(operands); +IRTypeLayout::Builder::Builder(IRBuilder* irBuilder) + : m_irBuilder(irBuilder) +{ +} - return irBuilder->getTypeLayout( - getOp(), - operands); - } +void IRTypeLayout::Builder::addResourceUsage(LayoutResourceKind kind, LayoutSize size) +{ + auto& resInfo = m_resInfos[Int(kind)]; + resInfo.kind = kind; + resInfo.size += size; +} + +void IRTypeLayout::Builder::addResourceUsage(IRTypeSizeAttr* sizeAttr) +{ + addResourceUsage(sizeAttr->getResourceKind(), sizeAttr->getSize()); +} - void IRTypeLayout::Builder::addOperands(List<IRInst*>& operands) +void IRTypeLayout::Builder::addResourceUsageFrom(IRTypeLayout* typeLayout) +{ + for (auto sizeAttr : typeLayout->getSizeAttrs()) { - addOperandsImpl(operands); + addResourceUsage(sizeAttr); } +} - void IRTypeLayout::Builder::addAttrs(List<IRInst*>& operands) - { - auto irBuilder = getIRBuilder(); +IRTypeLayout* IRTypeLayout::Builder::build() +{ + IRBuilder* irBuilder = getIRBuilder(); - for(auto resInfo : m_resInfos) - { - if(resInfo.kind == LayoutResourceKind::None) - continue; + List<IRInst*> operands; - IRInst* sizeAttr = irBuilder->getTypeSizeAttr( - resInfo.kind, - resInfo.size); - operands.add(sizeAttr); - } + addOperands(operands); + addAttrs(operands); - if( auto pendingTypeLayout = m_pendingTypeLayout ) - { - operands.add(irBuilder->getPendingLayoutAttr( - pendingTypeLayout)); - } + return irBuilder->getTypeLayout(getOp(), operands); +} - addAttrsImpl(operands); - } +void IRTypeLayout::Builder::addOperands(List<IRInst*>& operands) +{ + addOperandsImpl(operands); +} - // - // IRParameterGroupTypeLayout - // +void IRTypeLayout::Builder::addAttrs(List<IRInst*>& operands) +{ + auto irBuilder = getIRBuilder(); - void IRParameterGroupTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) + for (auto resInfo : m_resInfos) { - ioOperands.add(m_containerVarLayout); - ioOperands.add(m_elementVarLayout); - ioOperands.add(m_offsetElementTypeLayout); + if (resInfo.kind == LayoutResourceKind::None) + continue; + + IRInst* sizeAttr = irBuilder->getTypeSizeAttr(resInfo.kind, resInfo.size); + operands.add(sizeAttr); } - IRParameterGroupTypeLayout* IRParameterGroupTypeLayout::Builder::build() + if (auto pendingTypeLayout = m_pendingTypeLayout) { - return cast<IRParameterGroupTypeLayout>(Super::Builder::build()); + operands.add(irBuilder->getPendingLayoutAttr(pendingTypeLayout)); } - // - // IRStructTypeLayout - // + addAttrsImpl(operands); +} - void IRStructTypeLayout::Builder::addAttrsImpl(List<IRInst*>& ioOperands) - { - auto irBuilder = getIRBuilder(); - for(auto field : m_fields) - { - ioOperands.add( - irBuilder->getFieldLayoutAttr(field.key, field.layout)); - } - } +// +// IRParameterGroupTypeLayout +// - // - // IRTupleTypeLayout - // +void IRParameterGroupTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) +{ + ioOperands.add(m_containerVarLayout); + ioOperands.add(m_elementVarLayout); + ioOperands.add(m_offsetElementTypeLayout); +} - void IRTupleTypeLayout::Builder::addAttrsImpl(List<IRInst*>& ioOperands) - { - auto irBuilder = getIRBuilder(); - for(auto field : m_fields) - { - ioOperands.add(irBuilder->getTupleFieldLayoutAttr(field.layout)); - } - } +IRParameterGroupTypeLayout* IRParameterGroupTypeLayout::Builder::build() +{ + return cast<IRParameterGroupTypeLayout>(Super::Builder::build()); +} - // - // IRArrayTypeLayout - // +// +// IRStructTypeLayout +// - void IRArrayTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) +void IRStructTypeLayout::Builder::addAttrsImpl(List<IRInst*>& ioOperands) +{ + auto irBuilder = getIRBuilder(); + for (auto field : m_fields) { - ioOperands.add(m_elementTypeLayout); + ioOperands.add(irBuilder->getFieldLayoutAttr(field.key, field.layout)); } +} - // - // IRStructuredBufferTypeLayout - // +// +// IRTupleTypeLayout +// - void IRStructuredBufferTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) +void IRTupleTypeLayout::Builder::addAttrsImpl(List<IRInst*>& ioOperands) +{ + auto irBuilder = getIRBuilder(); + for (auto field : m_fields) { - ioOperands.add(m_elementTypeLayout); + ioOperands.add(irBuilder->getTupleFieldLayoutAttr(field.layout)); } +} - // - // IRPointerTypeLayout - // +// +// IRArrayTypeLayout +// - void IRPointerTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) - { - SLANG_UNUSED(ioOperands); - // TODO(JS): For now we don't store the value types layout to avoid - // infinite recursion. - //ioOperands.add(m_valueTypeLayout); - } +void IRArrayTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) +{ + ioOperands.add(m_elementTypeLayout); +} - // - // IRStreamOutputTypeLayout - // +// +// IRStructuredBufferTypeLayout +// - void IRStreamOutputTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) - { - ioOperands.add(m_elementTypeLayout); - } +void IRStructuredBufferTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) +{ + ioOperands.add(m_elementTypeLayout); +} - // - // IRMatrixTypeLayout - // +// +// IRPointerTypeLayout +// - IRMatrixTypeLayout::Builder::Builder(IRBuilder* irBuilder, MatrixLayoutMode mode) - : Super::Builder(irBuilder) - { - m_modeInst = irBuilder->getIntValue(irBuilder->getIntType(), IRIntegerValue(mode)); - } +void IRPointerTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) +{ + SLANG_UNUSED(ioOperands); + // TODO(JS): For now we don't store the value types layout to avoid + // infinite recursion. + // ioOperands.add(m_valueTypeLayout); +} - void IRMatrixTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) - { - ioOperands.add(m_modeInst); - } +// +// IRStreamOutputTypeLayout +// - // - // IRVarLayout - // +void IRStreamOutputTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) +{ + ioOperands.add(m_elementTypeLayout); +} - bool IRVarLayout::usesResourceKind(LayoutResourceKind kind) - { - // TODO: basing this check on whether or not the - // var layout has an entry for `kind` means that - // we can't just optimize away any entry where - // the offset is zero (which might be a small - // but nice optimization). We could consider shifting - // this test to use the entries on the type layout - // instead (since non-zero resource consumption - // should be an equivalent test). +// +// IRMatrixTypeLayout +// - return findOffsetAttr(kind) != nullptr; - } +IRMatrixTypeLayout::Builder::Builder(IRBuilder* irBuilder, MatrixLayoutMode mode) + : Super::Builder(irBuilder) +{ + m_modeInst = irBuilder->getIntValue(irBuilder->getIntType(), IRIntegerValue(mode)); +} - bool IRVarLayout::usesResourceFromKinds(LayoutResourceKindFlags kindFlags) - { - // Like usesResourceKind this works because there is an offset stored even if it's 0. - if (kindFlags) - { - for (auto offsetAttr : getOffsetAttrs()) - { - if (LayoutResourceKindFlag::make(offsetAttr->getResourceKind()) & kindFlags ) - return true; - } - } - return false; - } +void IRMatrixTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) +{ + ioOperands.add(m_modeInst); +} - IRSystemValueSemanticAttr* IRVarLayout::findSystemValueSemanticAttr() - { - return findAttr<IRSystemValueSemanticAttr>(); - } +// +// IRVarLayout +// - IRVarOffsetAttr* IRVarLayout::findOffsetAttr(LayoutResourceKind kind) +bool IRVarLayout::usesResourceKind(LayoutResourceKind kind) +{ + // TODO: basing this check on whether or not the + // var layout has an entry for `kind` means that + // we can't just optimize away any entry where + // the offset is zero (which might be a small + // but nice optimization). We could consider shifting + // this test to use the entries on the type layout + // instead (since non-zero resource consumption + // should be an equivalent test). + + return findOffsetAttr(kind) != nullptr; +} + +bool IRVarLayout::usesResourceFromKinds(LayoutResourceKindFlags kindFlags) +{ + // Like usesResourceKind this works because there is an offset stored even if it's 0. + if (kindFlags) { - for( auto offsetAttr : getOffsetAttrs() ) + for (auto offsetAttr : getOffsetAttrs()) { - if(offsetAttr->getResourceKind() == kind) - return offsetAttr; + if (LayoutResourceKindFlag::make(offsetAttr->getResourceKind()) & kindFlags) + return true; } - return nullptr; } + return false; +} - IROperandList<IRVarOffsetAttr> IRVarLayout::getOffsetAttrs() - { - return findAttrs<IRVarOffsetAttr>(); - } +IRSystemValueSemanticAttr* IRVarLayout::findSystemValueSemanticAttr() +{ + return findAttr<IRSystemValueSemanticAttr>(); +} - Stage IRVarLayout::getStage() +IRVarOffsetAttr* IRVarLayout::findOffsetAttr(LayoutResourceKind kind) +{ + for (auto offsetAttr : getOffsetAttrs()) { - if(auto stageAttr = findAttr<IRStageAttr>()) - return stageAttr->getStage(); - return Stage::Unknown; + if (offsetAttr->getResourceKind() == kind) + return offsetAttr; } + return nullptr; +} - IRVarLayout* IRVarLayout::getPendingVarLayout() - { - if( auto pendingLayoutAttr = findAttr<IRPendingLayoutAttr>() ) - { - return cast<IRVarLayout>(pendingLayoutAttr->getLayout()); - } - return nullptr; - } +IROperandList<IRVarOffsetAttr> IRVarLayout::getOffsetAttrs() +{ + return findAttrs<IRVarOffsetAttr>(); +} - IRVarLayout::Builder::Builder( - IRBuilder* irBuilder, - IRTypeLayout* typeLayout) - : m_irBuilder(irBuilder) - , m_typeLayout(typeLayout) - {} +Stage IRVarLayout::getStage() +{ + if (auto stageAttr = findAttr<IRStageAttr>()) + return stageAttr->getStage(); + return Stage::Unknown; +} - bool IRVarLayout::Builder::usesResourceKind(LayoutResourceKind kind) +IRVarLayout* IRVarLayout::getPendingVarLayout() +{ + if (auto pendingLayoutAttr = findAttr<IRPendingLayoutAttr>()) { - return m_resInfos[Int(kind)].kind != LayoutResourceKind::None; + return cast<IRVarLayout>(pendingLayoutAttr->getLayout()); } + return nullptr; +} - IRVarLayout::Builder::ResInfo* IRVarLayout::Builder::findOrAddResourceInfo(LayoutResourceKind kind) - { - auto& resInfo = m_resInfos[Int(kind)]; - resInfo.kind = kind; - return &resInfo; - } +IRVarLayout::Builder::Builder(IRBuilder* irBuilder, IRTypeLayout* typeLayout) + : m_irBuilder(irBuilder), m_typeLayout(typeLayout) +{ +} - void IRVarLayout::Builder::setSystemValueSemantic(String const& name, UInt index) - { - m_systemValueSemantic = getIRBuilder()->getSystemValueSemanticAttr(name, index); - } +bool IRVarLayout::Builder::usesResourceKind(LayoutResourceKind kind) +{ + return m_resInfos[Int(kind)].kind != LayoutResourceKind::None; +} - void IRVarLayout::Builder::setUserSemantic(String const& name, UInt index) - { - m_userSemantic = getIRBuilder()->getUserSemanticAttr(name, index); - } +IRVarLayout::Builder::ResInfo* IRVarLayout::Builder::findOrAddResourceInfo(LayoutResourceKind kind) +{ + auto& resInfo = m_resInfos[Int(kind)]; + resInfo.kind = kind; + return &resInfo; +} - void IRVarLayout::Builder::setStage(Stage stage) - { - m_stageAttr = getIRBuilder()->getStageAttr(stage); - } +void IRVarLayout::Builder::setSystemValueSemantic(String const& name, UInt index) +{ + m_systemValueSemantic = getIRBuilder()->getSystemValueSemanticAttr(name, index); +} - void IRVarLayout::Builder::cloneEverythingButOffsetsFrom( - IRVarLayout* that) - { - if(auto systemValueSemantic = that->findAttr<IRSystemValueSemanticAttr>()) - m_systemValueSemantic = systemValueSemantic; +void IRVarLayout::Builder::setUserSemantic(String const& name, UInt index) +{ + m_userSemantic = getIRBuilder()->getUserSemanticAttr(name, index); +} - if(auto userSemantic = that->findAttr<IRUserSemanticAttr>()) - m_userSemantic = userSemantic; +void IRVarLayout::Builder::setStage(Stage stage) +{ + m_stageAttr = getIRBuilder()->getStageAttr(stage); +} - if(auto stageAttr = that->findAttr<IRStageAttr>()) - m_stageAttr = stageAttr; - } +void IRVarLayout::Builder::cloneEverythingButOffsetsFrom(IRVarLayout* that) +{ + if (auto systemValueSemantic = that->findAttr<IRSystemValueSemanticAttr>()) + m_systemValueSemantic = systemValueSemantic; - IRVarLayout* IRVarLayout::Builder::build() - { - SLANG_ASSERT(m_typeLayout); + if (auto userSemantic = that->findAttr<IRUserSemanticAttr>()) + m_userSemantic = userSemantic; - IRBuilder* irBuilder = getIRBuilder(); + if (auto stageAttr = that->findAttr<IRStageAttr>()) + m_stageAttr = stageAttr; +} - List<IRInst*> operands; +IRVarLayout* IRVarLayout::Builder::build() +{ + SLANG_ASSERT(m_typeLayout); - operands.add(m_typeLayout); + IRBuilder* irBuilder = getIRBuilder(); - for(auto resInfo : m_resInfos) - { - if(resInfo.kind == LayoutResourceKind::None) - continue; + List<IRInst*> operands; - IRInst* varOffsetAttr = irBuilder->getVarOffsetAttr( - resInfo.kind, - resInfo.offset, - resInfo.space); - operands.add(varOffsetAttr); - } + operands.add(m_typeLayout); + + for (auto resInfo : m_resInfos) + { + if (resInfo.kind == LayoutResourceKind::None) + continue; - if(auto semanticAttr = m_userSemantic) - operands.add(semanticAttr); + IRInst* varOffsetAttr = + irBuilder->getVarOffsetAttr(resInfo.kind, resInfo.offset, resInfo.space); + operands.add(varOffsetAttr); + } - if(auto semanticAttr = m_systemValueSemantic) - operands.add(semanticAttr); + if (auto semanticAttr = m_userSemantic) + operands.add(semanticAttr); - if(auto stageAttr = m_stageAttr) - operands.add(stageAttr); + if (auto semanticAttr = m_systemValueSemantic) + operands.add(semanticAttr); - if(auto pendingVarLayout = m_pendingVarLayout) - { - IRInst* pendingLayoutAttr = irBuilder->getPendingLayoutAttr( - pendingVarLayout); - operands.add(pendingLayoutAttr); - } + if (auto stageAttr = m_stageAttr) + operands.add(stageAttr); - return irBuilder->getVarLayout(operands); + if (auto pendingVarLayout = m_pendingVarLayout) + { + IRInst* pendingLayoutAttr = irBuilder->getPendingLayoutAttr(pendingVarLayout); + operands.add(pendingLayoutAttr); } - // - // IREntryPointLayout - // + return irBuilder->getVarLayout(operands); +} - IRStructTypeLayout* getScopeStructLayout(IREntryPointLayout* scopeLayout) - { - auto scopeTypeLayout = scopeLayout->getParamsLayout()->getTypeLayout(); +// +// IREntryPointLayout +// - if( auto constantBufferTypeLayout = as<IRParameterGroupTypeLayout>(scopeTypeLayout) ) - { - scopeTypeLayout = constantBufferTypeLayout->getOffsetElementTypeLayout(); - } +IRStructTypeLayout* getScopeStructLayout(IREntryPointLayout* scopeLayout) +{ + auto scopeTypeLayout = scopeLayout->getParamsLayout()->getTypeLayout(); - if( auto structTypeLayout = as<IRStructTypeLayout>(scopeTypeLayout) ) - { - return structTypeLayout; - } + if (auto constantBufferTypeLayout = as<IRParameterGroupTypeLayout>(scopeTypeLayout)) + { + scopeTypeLayout = constantBufferTypeLayout->getOffsetElementTypeLayout(); + } - SLANG_UNEXPECTED("uhandled global-scope binding layout"); - UNREACHABLE_RETURN(nullptr); + if (auto structTypeLayout = as<IRStructTypeLayout>(scopeTypeLayout)) + { + return structTypeLayout; } - // + SLANG_UNEXPECTED("uhandled global-scope binding layout"); + UNREACHABLE_RETURN(nullptr); +} + +// - IRInst* IRInsertLoc::getParent() const +IRInst* IRInsertLoc::getParent() const +{ + auto inst = getInst(); + switch (getMode()) { - auto inst = getInst(); - switch(getMode()) - { - default: - case Mode::None: - return nullptr; - case Mode::Before: - case Mode::After: - return inst->getParent(); - case Mode::AtStart: - case Mode::AtEnd: - return inst; - } + default: + case Mode::None: return nullptr; + case Mode::Before: + case Mode::After: return inst->getParent(); + case Mode::AtStart: + case Mode::AtEnd: return inst; } +} - IRBlock* IRInsertLoc::getBlock() const +IRBlock* IRInsertLoc::getBlock() const +{ + return as<IRBlock>(getParent()); +} + +// Get the current function (or other value with code) +// that we are inserting into (if any). +IRInst* IRInsertLoc::getFunc() const +{ + auto pp = getParent(); + if (const auto block = as<IRBlock>(pp)) { - return as<IRBlock>(getParent()); + pp = pp->getParent(); } + if (as<IRGlobalValueWithCode>(pp) || as<IRExpand>(pp)) + return pp; + return nullptr; +} - // Get the current function (or other value with code) - // that we are inserting into (if any). - IRInst* IRInsertLoc::getFunc() const +void addHoistableInst(IRBuilder* builder, IRInst* inst); + +// Add an instruction into the current scope +void IRBuilder::addInst(IRInst* inst) +{ + if (getIROpInfo(inst->getOp()).isGlobal()) { - auto pp = getParent(); - if (const auto block = as<IRBlock>(pp)) - { - pp = pp->getParent(); - } - if (as<IRGlobalValueWithCode>(pp) || as<IRExpand>(pp)) - return pp; - return nullptr; + addHoistableInst(this, inst); + return; } - void addHoistableInst( - IRBuilder* builder, - IRInst* inst); + if (!inst->parent) + inst->insertAt(m_insertLoc); +} - // Add an instruction into the current scope - void IRBuilder::addInst( - IRInst* inst) +IRInst* IRBuilder::replaceOperand(IRUse* use, IRInst* newValue) +{ + auto user = use->getUser(); + if (user->getModule()) { - if (getIROpInfo(inst->getOp()).isGlobal()) - { - addHoistableInst(this, inst); - return; - } - - if (!inst->parent) - inst->insertAt(m_insertLoc); + user->getModule()->getDeduplicationContext()->getInstReplacementMap().tryGetValue( + newValue, + newValue); } - IRInst* IRBuilder::replaceOperand(IRUse* use, IRInst* newValue) + if (!getIROpInfo(user->getOp()).isHoistable()) { - auto user = use->getUser(); - if (user->getModule()) - { - user->getModule()->getDeduplicationContext()->getInstReplacementMap().tryGetValue(newValue, newValue); - } + use->set(newValue); + return user; + } - if (!getIROpInfo(user->getOp()).isHoistable()) - { - use->set(newValue); - return user; - } - - // If user is hoistable, we need to remove it from the global number map first, - // perform the update, then try to reinsert it back to the global number map. - // If we find an equivalent entry already exists in the global number map, - // we return the existing entry. - auto builder = user->getModule()->getDeduplicationContext(); - builder->_removeGlobalNumberingEntry(user); - use->init(user, newValue); - - IRInst* existingVal = nullptr; - if (builder->getGlobalValueNumberingMap().tryGetValue(IRInstKey{ user }, existingVal)) - { - user->replaceUsesWith(existingVal); - return existingVal; - } - else - { - builder->_addGlobalNumberingEntry(user); - return user; - } + // If user is hoistable, we need to remove it from the global number map first, + // perform the update, then try to reinsert it back to the global number map. + // If we find an equivalent entry already exists in the global number map, + // we return the existing entry. + auto builder = user->getModule()->getDeduplicationContext(); + builder->_removeGlobalNumberingEntry(user); + use->init(user, newValue); + + IRInst* existingVal = nullptr; + if (builder->getGlobalValueNumberingMap().tryGetValue(IRInstKey{user}, existingVal)) + { + user->replaceUsesWith(existingVal); + return existingVal; + } + else + { + builder->_addGlobalNumberingEntry(user); + return user; } +} - // Given two parent instructions, pick the better one to use as as - // insertion location for a "hoistable" instruction. +// Given two parent instructions, pick the better one to use as as +// insertion location for a "hoistable" instruction. +// +IRInst* mergeCandidateParentsForHoistableInst(IRInst* left, IRInst* right) +{ + // If the candidates are both the same, then who cares? + if (left == right) + return left; + + // If either `left` or `right` is a block, then we need to be + // a bit careful, because blocks can see other values just using + // the dominance relationship, without a direct parent-child relationship. + // + // First, check if each of `left` and `right` is a block. + // + auto leftBlock = as<IRBlock>(left); + auto rightBlock = as<IRBlock>(right); // - IRInst* mergeCandidateParentsForHoistableInst(IRInst* left, IRInst* right) + // As a special case, if both of these are blocks in the same parent, + // then we need to pick between them based on dominance. + // + if (leftBlock && rightBlock && (leftBlock->getParent() == rightBlock->getParent())) { - // If the candidates are both the same, then who cares? - if(left == right) return left; - - // If either `left` or `right` is a block, then we need to be - // a bit careful, because blocks can see other values just using - // the dominance relationship, without a direct parent-child relationship. - // - // First, check if each of `left` and `right` is a block. + // We assume that the order of basic blocks in a function is compatible + // with the dominance relationship (that is, if A dominates B, then + // A comes before B in the list of blocks), so it suffices to pick + // the *later* of the two blocks. // - auto leftBlock = as<IRBlock>(left); - auto rightBlock = as<IRBlock>(right); + // There are ways we could try to speed up this search, but no matter + // what it will be O(n) in the number of blocks, unless we build + // an explicit dominator tree, which is infeasible during IR building. + // Thus we just do a simple linear walk here. // - // As a special case, if both of these are blocks in the same parent, - // then we need to pick between them based on dominance. + // We will start at `leftBlock` and walk forward, until either... // - if (leftBlock && rightBlock && (leftBlock->getParent() == rightBlock->getParent())) + for (auto ll = leftBlock; ll; ll = ll->getNextBlock()) { - // We assume that the order of basic blocks in a function is compatible - // with the dominance relationship (that is, if A dominates B, then - // A comes before B in the list of blocks), so it suffices to pick - // the *later* of the two blocks. - // - // There are ways we could try to speed up this search, but no matter - // what it will be O(n) in the number of blocks, unless we build - // an explicit dominator tree, which is infeasible during IR building. - // Thus we just do a simple linear walk here. - // - // We will start at `leftBlock` and walk forward, until either... - // - for (auto ll = leftBlock; ll; ll = ll->getNextBlock()) - { - // ... we see `rightBlock` (in which case `rightBlock` came later), or ... - // - if (ll == rightBlock) return rightBlock; - } - // - // ... we run out of blocks (in which case `leftBlock` came later). + // ... we see `rightBlock` (in which case `rightBlock` came later), or ... // - return leftBlock; + if (ll == rightBlock) + return rightBlock; } - - // - // If the special case above doesn't apply, then `left` or `right` might - // still be a block, but they aren't blocks nested in the same function. - // We will find the first non-block ancestor of `left` and/or `right`. - // This will either be the inst itself (it is isn't a block), or - // its immediate parent (if it *is* a block). // - auto leftNonBlock = leftBlock ? leftBlock->getParent() : left; - auto rightNonBlock = rightBlock ? rightBlock->getParent() : right; - - // If either side is null, then take the non-null one. + // ... we run out of blocks (in which case `leftBlock` came later). // - if (!leftNonBlock) return right; - if (!rightNonBlock) return left; + return leftBlock; + } - // If the non-block on the left or right is a descendent of - // the other, then that is what we should use. - // - IRInst* parentNonBlock = nullptr; - for (auto ll = leftNonBlock; ll; ll = ll->getParent()) - { - if (ll == rightNonBlock) - { - parentNonBlock = leftNonBlock; - break; - } - } - for (auto rr = rightNonBlock; rr; rr = rr->getParent()) - { - if (rr == leftNonBlock) - { - SLANG_ASSERT(!parentNonBlock || parentNonBlock == leftNonBlock); - parentNonBlock = rightNonBlock; - break; - } - } + // + // If the special case above doesn't apply, then `left` or `right` might + // still be a block, but they aren't blocks nested in the same function. + // We will find the first non-block ancestor of `left` and/or `right`. + // This will either be the inst itself (it is isn't a block), or + // its immediate parent (if it *is* a block). + // + auto leftNonBlock = leftBlock ? leftBlock->getParent() : left; + auto rightNonBlock = rightBlock ? rightBlock->getParent() : right; - // As a matter of validity in the IR, we expect one - // of the two to be an ancestor (in the non-block case), - // because otherwise we'd be violating the basic dominance - // assumptions. - // - SLANG_ASSERT(parentNonBlock); + // If either side is null, then take the non-null one. + // + if (!leftNonBlock) + return right; + if (!rightNonBlock) + return left; - // As a fallback, try to use the left parent as a default - // in case things go badly. - // - if (!parentNonBlock) + // If the non-block on the left or right is a descendent of + // the other, then that is what we should use. + // + IRInst* parentNonBlock = nullptr; + for (auto ll = leftNonBlock; ll; ll = ll->getParent()) + { + if (ll == rightNonBlock) { parentNonBlock = leftNonBlock; + break; } + } + for (auto rr = rightNonBlock; rr; rr = rr->getParent()) + { + if (rr == leftNonBlock) + { + SLANG_ASSERT(!parentNonBlock || parentNonBlock == leftNonBlock); + parentNonBlock = rightNonBlock; + break; + } + } - IRInst* parent = parentNonBlock; + // As a matter of validity in the IR, we expect one + // of the two to be an ancestor (in the non-block case), + // because otherwise we'd be violating the basic dominance + // assumptions. + // + SLANG_ASSERT(parentNonBlock); - // At this point we've found a non-block parent where we - // could stick things, but we have to fix things up in - // case we should be inserting into a block beneath - // that non-block parent. - if (leftBlock && (parentNonBlock == leftNonBlock)) - { - // We have a left block, and have picked its parent. + // As a fallback, try to use the left parent as a default + // in case things go badly. + // + if (!parentNonBlock) + { + parentNonBlock = leftNonBlock; + } - // It cannot be the case that there is a right block - // with the same parent, or else our special case - // would have triggered at the start. - SLANG_ASSERT(!rightBlock || (parentNonBlock != rightNonBlock)); + IRInst* parent = parentNonBlock; - parent = leftBlock; - } - else if (rightBlock && (parentNonBlock == rightNonBlock)) - { - // We have a right block, and have picked its parent. + // At this point we've found a non-block parent where we + // could stick things, but we have to fix things up in + // case we should be inserting into a block beneath + // that non-block parent. + if (leftBlock && (parentNonBlock == leftNonBlock)) + { + // We have a left block, and have picked its parent. - // We already tested above, so we know there isn't a - // matching situation on the left side. + // It cannot be the case that there is a right block + // with the same parent, or else our special case + // would have triggered at the start. + SLANG_ASSERT(!rightBlock || (parentNonBlock != rightNonBlock)); - parent = rightBlock; - } + parent = leftBlock; + } + else if (rightBlock && (parentNonBlock == rightNonBlock)) + { + // We have a right block, and have picked its parent. - // Okay, we've picked the parent we want to insert into, - // *but* one last special case arises, because an `IRGlobalValueWithCode` - // is not actually a suitable place to insert instructions. - // Furthermore, there is no actual need to insert instructions at - // that scope, because any parameters, etc. are actually attached - // to the block(s) within the function. - if (auto parentFunc = as<IRGlobalValueWithCode>(parent)) - { - // Insert in the parent of the function (or other value with code). - // We know that the parent must be able to hold ordinary instructions, - // because it was able to hold this `IRGlobalValueWithCode` - parent = parentFunc->getParent(); - } + // We already tested above, so we know there isn't a + // matching situation on the left side. - return parent; + parent = rightBlock; } - IRInst* IRModule::_allocateInst( - IROp op, - Int operandCount, - size_t minSizeInBytes) + // Okay, we've picked the parent we want to insert into, + // *but* one last special case arises, because an `IRGlobalValueWithCode` + // is not actually a suitable place to insert instructions. + // Furthermore, there is no actual need to insert instructions at + // that scope, because any parameters, etc. are actually attached + // to the block(s) within the function. + if (auto parentFunc = as<IRGlobalValueWithCode>(parent)) { - // There are two basic cases for instructions that affect how we compute size: - // - // * The default case is that an instruction's state is fully defined by the fields - // in the `IRInst` base type, along with the trailing operand list (a tail-allocated - // array of `IRUse`s. Almost all instructions need space allocated this way. - // - // * A small number of cases (currently `IRConstant`s and the `IRModule` type) have - // *zero* operands but include additional state beyond the fields in `IRInst`. - // For these cases we want to ensure that at least `sizeof(T)` bytes are allocated, - // based on the specific leaf type `T`. - // - // We handle the combination of the two cases by just taking the maximum of the two - // different sizes. - // - size_t defaultSize = sizeof(IRInst) + (operandCount) * sizeof(IRUse); - size_t totalSize = minSizeInBytes > defaultSize ? minSizeInBytes : defaultSize; + // Insert in the parent of the function (or other value with code). + // We know that the parent must be able to hold ordinary instructions, + // because it was able to hold this `IRGlobalValueWithCode` + parent = parentFunc->getParent(); + } - IRInst* inst = (IRInst*) m_memoryArena.allocateAndZero(totalSize); + return parent; +} - // TODO: Is it actually important to run a constructor here? - new(inst) IRInst(); +IRInst* IRModule::_allocateInst(IROp op, Int operandCount, size_t minSizeInBytes) +{ + // There are two basic cases for instructions that affect how we compute size: + // + // * The default case is that an instruction's state is fully defined by the fields + // in the `IRInst` base type, along with the trailing operand list (a tail-allocated + // array of `IRUse`s. Almost all instructions need space allocated this way. + // + // * A small number of cases (currently `IRConstant`s and the `IRModule` type) have + // *zero* operands but include additional state beyond the fields in `IRInst`. + // For these cases we want to ensure that at least `sizeof(T)` bytes are allocated, + // based on the specific leaf type `T`. + // + // We handle the combination of the two cases by just taking the maximum of the two + // different sizes. + // + size_t defaultSize = sizeof(IRInst) + (operandCount) * sizeof(IRUse); + size_t totalSize = minSizeInBytes > defaultSize ? minSizeInBytes : defaultSize; - inst->operandCount = uint32_t(operandCount); - inst->m_op = op; + IRInst* inst = (IRInst*)m_memoryArena.allocateAndZero(totalSize); - return inst; - } + // TODO: Is it actually important to run a constructor here? + new (inst) IRInst(); + + inst->operandCount = uint32_t(operandCount); + inst->m_op = op; - /// Return whichever of `left` or `right` represents the later point in a common parent - static IRInst* pickLaterInstInSameParent( - IRInst* left, - IRInst* right) + return inst; +} + +/// Return whichever of `left` or `right` represents the later point in a common parent +static IRInst* pickLaterInstInSameParent(IRInst* left, IRInst* right) +{ + // When using instructions to represent insertion locations, + // a null instruction represents the end of the parent block, + // so if either of the two instructions is null, it indicates + // the end of the parent, and thus comes later. + // + if (!left) + return nullptr; + if (!right) + return nullptr; + + // In the non-null case, we must have the precondition that + // the two candidates have the same parent. + // + SLANG_ASSERT(left->getParent() == right->getParent()); + + // No matter what, figuring out which instruction comes first + // is a linear-time operation in the number of instructions + // in the same parent, but we can optimize based on the + // assumption that in common cases one of the following will + // hold: + // + // * `left` and `right` are close to one another in the IR + // * `left` and/or `right` is close to the start of its parent + // + // To optimize for those conditions, we create two cursors that + // start at `left` and `right` respectively, and scan backward. + // + auto ll = left; + auto rr = right; + for (;;) { - // When using instructions to represent insertion locations, - // a null instruction represents the end of the parent block, - // so if either of the two instructions is null, it indicates - // the end of the parent, and thus comes later. + // If one of the cursors runs into the other while scanning + // backwards, then it implies it must have been the later + // of the two. // - if(!left) return nullptr; - if(!right) return nullptr; - - // In the non-null case, we must have the precondition that - // the two candidates have the same parent. + // This is our early-exit condition for `left` and `right` + // being close together. + // + // Note: this condition will trigger on the first iteration + // in the case where `left == right`. + // + if (ll == right) + return left; + if (rr == left) + return right; + + // If one of the cursors reaches the start of the block, + // then that implies it started at the earlier position. + // In that case, the other candidate must be the later + // one. + // + // This is the early-exit condition for `left` and/or `right` + // being close to the start of the parent. // - SLANG_ASSERT(left->getParent() == right->getParent()); + if (!ll) + return right; + if (!rr) + return left; - // No matter what, figuring out which instruction comes first - // is a linear-time operation in the number of instructions - // in the same parent, but we can optimize based on the - // assumption that in common cases one of the following will - // hold: + // Otherwise, we move both cursors backward and continue + // the search. // - // * `left` and `right` are close to one another in the IR - // * `left` and/or `right` is close to the start of its parent + ll = ll->getPrevInst(); + rr = rr->getPrevInst(); + + // Note: in the worst case, one of the cursors is + // at the end of the parent, and the other is halfway + // through, so that each cursor needs to visit half + // of the instructions in the parent before we reach + // one of our termination conditions. // - // To optimize for those conditions, we create two cursors that - // start at `left` and `right` respectively, and scan backward. + // As a result the worst-case running time is still O(N), + // and there is nothing we can do to improve that + // with our linked-list representation. // - auto ll = left; - auto rr = right; - for(;;) - { - // If one of the cursors runs into the other while scanning - // backwards, then it implies it must have been the later - // of the two. - // - // This is our early-exit condition for `left` and `right` - // being close together. - // - // Note: this condition will trigger on the first iteration - // in the case where `left == right`. - // - if(ll == right) return left; - if(rr == left) return right; + // If the assumptions given turn out to be wrong, and + // we find that a common case is instructions close + // to the *end* of a block, we can either flip the + // direction that the cursors traverse, or even add + // two more cursors that scan forward instead of + // backward. + } +} + +// Given an instruction that represents a constant, a type, etc. +// Try to "hoist" it as far toward the global scope as possible +// to insert it at a location where it will be maximally visible. +// +void addHoistableInst(IRBuilder* builder, IRInst* inst) +{ + // Start with the assumption that we would insert this instruction + // into the global scope (the instruction that represents the module) + IRInst* parent = builder->getModule()->getModuleInst(); - // If one of the cursors reaches the start of the block, - // then that implies it started at the earlier position. - // In that case, the other candidate must be the later - // one. - // - // This is the early-exit condition for `left` and/or `right` - // being close to the start of the parent. - // - if(!ll) return right; - if(!rr) return left; + // The above decision might be invalid, because there might be + // one or more operands of the instruction that are defined in + // more deeply nested parents than the global scope. + // + // Therefore, we will scan the operands of the instruction, and + // look at the parents that define them. + // + UInt operandCount = inst->getOperandCount(); + for (UInt ii = 0; ii < operandCount; ++ii) + { + auto operand = inst->getOperand(ii); + if (!operand) + continue; - // Otherwise, we move both cursors backward and continue - // the search. - // - ll = ll->getPrevInst(); - rr = rr->getPrevInst(); - - // Note: in the worst case, one of the cursors is - // at the end of the parent, and the other is halfway - // through, so that each cursor needs to visit half - // of the instructions in the parent before we reach - // one of our termination conditions. - // - // As a result the worst-case running time is still O(N), - // and there is nothing we can do to improve that - // with our linked-list representation. - // - // If the assumptions given turn out to be wrong, and - // we find that a common case is instructions close - // to the *end* of a block, we can either flip the - // direction that the cursors traverse, or even add - // two more cursors that scan forward instead of - // backward. - } + auto operandParent = operand->getParent(); + + parent = mergeCandidateParentsForHoistableInst(parent, operandParent); + } + if (inst->getFullType()) + { + parent = mergeCandidateParentsForHoistableInst(parent, inst->getFullType()->getParent()); } - // Given an instruction that represents a constant, a type, etc. - // Try to "hoist" it as far toward the global scope as possible - // to insert it at a location where it will be maximally visible. + // We better have ended up with a parent to insert into, + // or else the invariants of our IR have been violated. // - void addHoistableInst( - IRBuilder* builder, - IRInst* inst) - { - // Start with the assumption that we would insert this instruction - // into the global scope (the instruction that represents the module) - IRInst* parent = builder->getModule()->getModuleInst(); + SLANG_ASSERT(parent); - // The above decision might be invalid, because there might be - // one or more operands of the instruction that are defined in - // more deeply nested parents than the global scope. - // - // Therefore, we will scan the operands of the instruction, and - // look at the parents that define them. + // Once we determine the parent instruction that the + // new instruction should be inserted into, we need + // to find an appropriate place to insert it. + // + // There are two concerns at play here, both of which + // stem from the property that within a block we + // require definitions to precede their uses. + // + // The first concern is that we want to emit a + // "hoistable" instruction like a type as early as possible, + // so that if a subsequent optimization pass requests + // the same type/value again, it doesn't get a cached/deduplicated + // pointer to an instruction that comes after the code being + // processed. + // + // The second concern is that we must emit any hoistable + // instruction after any of its operands (or its type) + // if they come from the same block/parent. + // + // These two conditions together indicate that we want + // to insert the instruction right after whichever of + // its operands come last in the parent block and if + // none of the operands come from the same block, we + // should try to insert it as early as possible in + // that block. + // + // We want to insert a hoistable instruction at the + // earliest possible point in its parent, which + // should be right after whichever of its operands + // is defined in that same block (if any) + // + // We will solve this problem by computing the + // earliest instruction that it would be valid for + // us to insert before. + // + // We start by considering insertion before the + // first instruction in the parent (if any) and + // then move the insertion point later as needed. + // + // Note: a null `insertBeforeInst` is used + // here to mean to insert at the end of the parent. + // + IRInst* insertBeforeInst = parent->getFirstChild(); + + // Hoistable instructions are always "ordinary" + // instructions, so they need to come after + // any parameters of the parent. + // + while (insertBeforeInst && insertBeforeInst->getOp() == kIROp_Param) + insertBeforeInst = insertBeforeInst->getNextInst(); + + // For instructions that will be placed at module scope, + // we don't care about relative ordering, but for everything + // else, we want to ensure that an instruction comes after + // its type and operands. + // + if (!as<IRModuleInst>(parent)) + { + // We need to make sure that if any of + // the operands of `inst` come from the same + // block that we insert after them. // - UInt operandCount = inst->getOperandCount(); for (UInt ii = 0; ii < operandCount; ++ii) { auto operand = inst->getOperand(ii); if (!operand) continue; - auto operandParent = operand->getParent(); + if (operand->getParent() != parent) + continue; - parent = mergeCandidateParentsForHoistableInst(parent, operandParent); - } - if (inst->getFullType()) - { - parent = mergeCandidateParentsForHoistableInst(parent, inst->getFullType()->getParent()); + insertBeforeInst = pickLaterInstInSameParent(insertBeforeInst, operand->getNextInst()); } - - // We better have ended up with a parent to insert into, - // or else the invariants of our IR have been violated. - // - SLANG_ASSERT(parent); - - // Once we determine the parent instruction that the - // new instruction should be inserted into, we need - // to find an appropriate place to insert it. - // - // There are two concerns at play here, both of which - // stem from the property that within a block we - // require definitions to precede their uses. - // - // The first concern is that we want to emit a - // "hoistable" instruction like a type as early as possible, - // so that if a subsequent optimization pass requests - // the same type/value again, it doesn't get a cached/deduplicated - // pointer to an instruction that comes after the code being - // processed. - // - // The second concern is that we must emit any hoistable - // instruction after any of its operands (or its type) - // if they come from the same block/parent. // - // These two conditions together indicate that we want - // to insert the instruction right after whichever of - // its operands come last in the parent block and if - // none of the operands come from the same block, we - // should try to insert it as early as possible in - // that block. + // Similarly, if the type of `inst` comes from + // the same parent, then we need to make sure + // we insert after the type. // - // We want to insert a hoistable instruction at the - // earliest possible point in its parent, which - // should be right after whichever of its operands - // is defined in that same block (if any) - // - // We will solve this problem by computing the - // earliest instruction that it would be valid for - // us to insert before. - // - // We start by considering insertion before the - // first instruction in the parent (if any) and - // then move the insertion point later as needed. - // - // Note: a null `insertBeforeInst` is used - // here to mean to insert at the end of the parent. - // - IRInst* insertBeforeInst = parent->getFirstChild(); - - // Hoistable instructions are always "ordinary" - // instructions, so they need to come after - // any parameters of the parent. - // - while (insertBeforeInst && insertBeforeInst->getOp() == kIROp_Param) - insertBeforeInst = insertBeforeInst->getNextInst(); - - // For instructions that will be placed at module scope, - // we don't care about relative ordering, but for everything - // else, we want to ensure that an instruction comes after - // its type and operands. - // - if( !as<IRModuleInst>(parent) ) + if (auto type = inst->getFullType()) { - // We need to make sure that if any of - // the operands of `inst` come from the same - // block that we insert after them. - // - for (UInt ii = 0; ii < operandCount; ++ii) + if (type->getParent() == parent) { - auto operand = inst->getOperand(ii); - if (!operand) - continue; - - if(operand->getParent() != parent) - continue; - - insertBeforeInst = pickLaterInstInSameParent(insertBeforeInst, operand->getNextInst()); + insertBeforeInst = pickLaterInstInSameParent(insertBeforeInst, type->getNextInst()); } - // - // Similarly, if the type of `inst` comes from - // the same parent, then we need to make sure - // we insert after the type. - // - if(auto type = inst->getFullType()) - { - if(type->getParent() == parent) - { - insertBeforeInst = pickLaterInstInSameParent(insertBeforeInst, type->getNextInst()); - } - } - } - - if( insertBeforeInst ) - { - inst->insertBefore(insertBeforeInst); - } - else - { - inst->insertAtEnd(parent); } } - void IRBuilder::_maybeSetSourceLoc( - IRInst* inst) + if (insertBeforeInst) { - auto sourceLocInfo = getSourceLocInfo(); - if(!sourceLocInfo) - return; + inst->insertBefore(insertBeforeInst); + } + else + { + inst->insertAtEnd(parent); + } +} - // Try to find something with usable location info - for(;;) - { - if(sourceLocInfo->sourceLoc.getRaw()) - break; +void IRBuilder::_maybeSetSourceLoc(IRInst* inst) +{ + auto sourceLocInfo = getSourceLocInfo(); + if (!sourceLocInfo) + return; - if(!sourceLocInfo->next) - break; + // Try to find something with usable location info + for (;;) + { + if (sourceLocInfo->sourceLoc.getRaw()) + break; - sourceLocInfo = sourceLocInfo->next; - } + if (!sourceLocInfo->next) + break; - inst->sourceLoc = sourceLocInfo->sourceLoc; + sourceLocInfo = sourceLocInfo->next; } + inst->sourceLoc = sourceLocInfo->sourceLoc; +} + #if SLANG_ENABLE_IR_BREAK_ALLOC - SLANG_API uint32_t _slangIRAllocBreak = 0xFFFFFFFF; - uint32_t& _debugGetIRAllocCounter() - { - static uint32_t counter = 0; - return counter; - } - uint32_t _debugGetAndIncreaseInstCounter() +SLANG_API uint32_t _slangIRAllocBreak = 0xFFFFFFFF; +uint32_t& _debugGetIRAllocCounter() +{ + static uint32_t counter = 0; + return counter; +} +uint32_t _debugGetAndIncreaseInstCounter() +{ + if (_slangIRAllocBreak != 0xFFFFFFFF && _debugGetIRAllocCounter() == _slangIRAllocBreak) { - if (_slangIRAllocBreak != 0xFFFFFFFF && _debugGetIRAllocCounter() == _slangIRAllocBreak) - { #if _WIN32 && defined(_MSC_VER) - __debugbreak(); + __debugbreak(); #endif - } - return _debugGetIRAllocCounter()++; } + return _debugGetIRAllocCounter()++; +} #endif - IRInst* IRBuilder::_createInst( - size_t minSizeInBytes, - IRType* type, - IROp op, - Int fixedArgCount, - IRInst* const* fixedArgs, - Int varArgListCount, - Int const* listArgCounts, - IRInst* const* const* listArgs) - { - IRInst* instReplacement = type; - m_dedupContext->getInstReplacementMap().tryGetValue(type, instReplacement); - type = (IRType*)instReplacement; - - if (getIROpInfo(op).flags & kIROpFlag_Hoistable) - { - return _findOrEmitHoistableInst(type, op, fixedArgCount, fixedArgs, varArgListCount, listArgCounts, listArgs); - } +IRInst* IRBuilder::_createInst( + size_t minSizeInBytes, + IRType* type, + IROp op, + Int fixedArgCount, + IRInst* const* fixedArgs, + Int varArgListCount, + Int const* listArgCounts, + IRInst* const* const* listArgs) +{ + IRInst* instReplacement = type; + m_dedupContext->getInstReplacementMap().tryGetValue(type, instReplacement); + type = (IRType*)instReplacement; - Int varArgCount = 0; - for (Int ii = 0; ii < varArgListCount; ++ii) - { - varArgCount += listArgCounts[ii]; - } + if (getIROpInfo(op).flags & kIROpFlag_Hoistable) + { + return _findOrEmitHoistableInst( + type, + op, + fixedArgCount, + fixedArgs, + varArgListCount, + listArgCounts, + listArgs); + } + + Int varArgCount = 0; + for (Int ii = 0; ii < varArgListCount; ++ii) + { + varArgCount += listArgCounts[ii]; + } - Int totalOperandCount = fixedArgCount + varArgCount; + Int totalOperandCount = fixedArgCount + varArgCount; - auto module = getModule(); - SLANG_ASSERT(module); - IRInst* inst = module->_allocateInst(op, totalOperandCount, minSizeInBytes); + auto module = getModule(); + SLANG_ASSERT(module); + IRInst* inst = module->_allocateInst(op, totalOperandCount, minSizeInBytes); #if SLANG_ENABLE_IR_BREAK_ALLOC - inst->_debugUID = _debugGetAndIncreaseInstCounter(); + inst->_debugUID = _debugGetAndIncreaseInstCounter(); #endif - inst->typeUse.init(inst, type); + inst->typeUse.init(inst, type); - _maybeSetSourceLoc(inst); + _maybeSetSourceLoc(inst); - auto operand = inst->getOperands(); + auto operand = inst->getOperands(); - for( Int aa = 0; aa < fixedArgCount; ++aa ) + for (Int aa = 0; aa < fixedArgCount; ++aa) + { + if (fixedArgs) + { + auto arg = fixedArgs[aa]; + m_dedupContext->getInstReplacementMap().tryGetValue(arg, arg); + operand->init(inst, arg); + } + else { - if (fixedArgs) + operand->init(inst, nullptr); + } + operand++; + } + + for (Int ii = 0; ii < varArgListCount; ++ii) + { + Int listArgCount = listArgCounts[ii]; + for (Int jj = 0; jj < listArgCount; ++jj) + { + if (listArgs[ii]) { - auto arg = fixedArgs[aa]; + auto arg = listArgs[ii][jj]; m_dedupContext->getInstReplacementMap().tryGetValue(arg, arg); operand->init(inst, arg); } @@ -1830,2077 +1836,1909 @@ namespace Slang } operand++; } - - for (Int ii = 0; ii < varArgListCount; ++ii) - { - Int listArgCount = listArgCounts[ii]; - for (Int jj = 0; jj < listArgCount; ++jj) - { - if (listArgs[ii]) - { - auto arg = listArgs[ii][jj]; - m_dedupContext->getInstReplacementMap().tryGetValue(arg, arg); - operand->init(inst, arg); - } - else - { - operand->init(inst, nullptr); - } - operand++; - } - } - return inst; } + return inst; +} + +// Create an IR instruction/value and initialize it. +// +// In this case `argCount` and `args` represent the +// arguments *after* the type (which is a mandatory +// argument for all instructions). +template<typename T> +static T* createInstImpl( + IRBuilder* builder, + IROp op, + IRType* type, + Int fixedArgCount, + IRInst* const* fixedArgs, + Int varArgListCount, + Int const* listArgCounts, + IRInst* const* const* listArgs) +{ + return (T*)builder->_createInst( + sizeof(T), + type, + op, + fixedArgCount, + fixedArgs, + varArgListCount, + listArgCounts, + listArgs); +} + +template<typename T> +static T* createInstImpl( + IRBuilder* builder, + IROp op, + IRType* type, + Int fixedArgCount, + IRInst* const* fixedArgs, + Int varArgCount = 0, + IRInst* const* varArgs = nullptr) +{ + return createInstImpl<T>( + builder, + op, + type, + fixedArgCount, + fixedArgs, + 1, + &varArgCount, + &varArgs); +} + +template<typename T> +static T* createInst(IRBuilder* builder, IROp op, IRType* type, Int argCount, IRInst* const* args) +{ + return createInstImpl<T>(builder, op, type, argCount, args); +} - // Create an IR instruction/value and initialize it. - // - // In this case `argCount` and `args` represent the - // arguments *after* the type (which is a mandatory - // argument for all instructions). - template<typename T> - static T* createInstImpl( - IRBuilder* builder, - IROp op, - IRType* type, - Int fixedArgCount, - IRInst* const* fixedArgs, - Int varArgListCount, - Int const* listArgCounts, - IRInst* const* const* listArgs) - { - return (T*) builder->_createInst( - sizeof(T), - type, - op, - fixedArgCount, - fixedArgs, - varArgListCount, - listArgCounts, - listArgs); - } +template<typename T> +static T* createInst(IRBuilder* builder, IROp op, IRType* type) +{ + return createInstImpl<T>(builder, op, type, 0, nullptr); +} - template<typename T> - static T* createInstImpl( - IRBuilder* builder, - IROp op, - IRType* type, - Int fixedArgCount, - IRInst* const* fixedArgs, - Int varArgCount = 0, - IRInst* const* varArgs = nullptr) - { - return createInstImpl<T>( - builder, - op, - type, - fixedArgCount, - fixedArgs, - 1, - &varArgCount, - &varArgs); - } +template<typename T> +static T* createInst(IRBuilder* builder, IROp op, IRType* type, IRInst* arg) +{ + return createInstImpl<T>(builder, op, type, 1, &arg); +} + +template<typename T> +static T* createInst(IRBuilder* builder, IROp op, IRType* type, IRInst* arg1, IRInst* arg2) +{ + IRInst* args[] = {arg1, arg2}; + return createInstImpl<T>(builder, op, type, 2, &args[0]); +} + +template<typename T> +static T* createInst( + IRBuilder* builder, + IROp op, + IRType* type, + IRInst* arg1, + IRInst* arg2, + IRInst* arg3) +{ + IRInst* args[] = {arg1, arg2, arg3}; + return createInstImpl<T>(builder, op, type, 3, &args[0]); +} + +template<typename T> +static T* createInstWithTrailingArgs( + IRBuilder* builder, + IROp op, + IRType* type, + Int argCount, + IRInst* const* args) +{ + return createInstImpl<T>(builder, op, type, argCount, args); +} + +template<typename T> +static T* createInstWithTrailingArgs( + IRBuilder* builder, + IROp op, + IRType* type, + Int fixedArgCount, + IRInst* const* fixedArgs, + Int varArgCount, + IRInst* const* varArgs) +{ + return createInstImpl<T>(builder, op, type, fixedArgCount, fixedArgs, varArgCount, varArgs); +} + +template<typename T> +static T* createInstWithTrailingArgs( + IRBuilder* builder, + IROp op, + IRType* type, + IRInst* arg1, + Int varArgCount, + IRInst* const* varArgs) +{ + IRInst* fixedArgs[] = {arg1}; + UInt fixedArgCount = sizeof(fixedArgs) / sizeof(fixedArgs[0]); + + return createInstImpl<T>(builder, op, type, fixedArgCount, fixedArgs, varArgCount, varArgs); +} +// - template<typename T> - static T* createInst( - IRBuilder* builder, - IROp op, - IRType* type, - Int argCount, - IRInst* const* args) +HashCode IRInstKey::_getHashCode() +{ + auto code = Slang::getHashCode(inst->getOp()); + code = combineHash(code, Slang::getHashCode(inst->getFullType())); + code = combineHash(code, Slang::getHashCode(inst->getOperandCount())); + + auto argCount = inst->getOperandCount(); + auto args = inst->getOperands(); + for (UInt aa = 0; aa < argCount; ++aa) { - return createInstImpl<T>( - builder, - op, - type, - argCount, - args); + code = combineHash(code, Slang::getHashCode(args[aa].get())); } + return code; +} - template<typename T> - static T* createInst( - IRBuilder* builder, - IROp op, - IRType* type) +UnownedStringSlice IRConstant::getStringSlice() +{ + assert(getOp() == kIROp_StringLit || getOp() == kIROp_BlobLit); + // If the transitory decoration is set, then this is uses the transitoryStringVal for the text + // storage. This is typically used when we are using a transitory IRInst held on the stack (such + // that it can be looked up in cached), that just points to a string elsewhere, and NOT the + // typical normal style, where the string is held after the instruction in memory. + // + if (findDecorationImpl(kIROp_TransitoryDecoration)) { - return createInstImpl<T>( - builder, - op, - type, - 0, - nullptr); + return UnownedStringSlice( + value.transitoryStringVal.chars, + value.transitoryStringVal.numChars); } - - template<typename T> - static T* createInst( - IRBuilder* builder, - IROp op, - IRType* type, - IRInst* arg) + else { - return createInstImpl<T>( - builder, - op, - type, - 1, - &arg); + return UnownedStringSlice(value.stringVal.chars, value.stringVal.numChars); } +} - template<typename T> - static T* createInst( - IRBuilder* builder, - IROp op, - IRType* type, - IRInst* arg1, - IRInst* arg2) +bool IRConstant::isFinite() const +{ + SLANG_ASSERT(getOp() == kIROp_FloatLit); + + // Lets check we can analyze as double, at least in principal + SLANG_COMPILE_TIME_ASSERT(sizeof(IRFloatingPointValue) == sizeof(double)); + // We are in effect going to type pun (yay!), lets make sure they are the same size + SLANG_COMPILE_TIME_ASSERT(sizeof(IRIntegerValue) == sizeof(IRFloatingPointValue)); + + const uint64_t i = uint64_t(value.intVal); + int e = int(i >> 52) & 0x7ff; + return (e != 0x7ff); +} + +IRConstant::FloatKind IRConstant::getFloatKind() const +{ + SLANG_ASSERT(getOp() == kIROp_FloatLit); + + const uint64_t i = uint64_t(value.intVal); + int e = int(i >> 52) & 0x7ff; + if (e == 0x7ff) { - IRInst* args[] = { arg1, arg2 }; - return createInstImpl<T>( - builder, - op, - type, - 2, - &args[0]); - } - - template<typename T> - static T* createInst( - IRBuilder* builder, - IROp op, - IRType* type, - IRInst* arg1, - IRInst* arg2, - IRInst* arg3) - { - IRInst* args[] = { arg1, arg2, arg3 }; - return createInstImpl<T>( - builder, - op, - type, - 3, - &args[0]); + if (i << 12) + { + return FloatKind::Nan; + } + // Sign bit (top bit) will indicate positive or negative nan + return value.intVal < 0 ? FloatKind::NegativeInfinity : FloatKind::PositiveInfinity; } + return FloatKind::Finite; +} - template<typename T> - static T* createInstWithTrailingArgs( - IRBuilder* builder, - IROp op, - IRType* type, - Int argCount, - IRInst* const* args) +bool IRConstant::isValueEqual(IRConstant* rhs) +{ + // If they are literally the same thing.. + if (this == rhs) { - return createInstImpl<T>( - builder, - op, - type, - argCount, - args); - } - - template<typename T> - static T* createInstWithTrailingArgs( - IRBuilder* builder, - IROp op, - IRType* type, - Int fixedArgCount, - IRInst* const* fixedArgs, - Int varArgCount, - IRInst* const* varArgs) - { - return createInstImpl<T>( - builder, - op, - type, - fixedArgCount, - fixedArgs, - varArgCount, - varArgs); + return true; } - - template<typename T> - static T* createInstWithTrailingArgs( - IRBuilder* builder, - IROp op, - IRType* type, - IRInst* arg1, - Int varArgCount, - IRInst* const* varArgs) + // Check the type and they are the same op & same type + if (getOp() != rhs->getOp()) { - IRInst* fixedArgs[] = { arg1 }; - UInt fixedArgCount = sizeof(fixedArgs) / sizeof(fixedArgs[0]); - - return createInstImpl<T>( - builder, - op, - type, - fixedArgCount, - fixedArgs, - varArgCount, - varArgs); + return false; } - // - HashCode IRInstKey::_getHashCode() + switch (getOp()) { - auto code = Slang::getHashCode(inst->getOp()); - code = combineHash(code, Slang::getHashCode(inst->getFullType())); - code = combineHash(code, Slang::getHashCode(inst->getOperandCount())); - - auto argCount = inst->getOperandCount(); - auto args = inst->getOperands(); - for( UInt aa = 0; aa < argCount; ++aa ) + case kIROp_BoolLit: + case kIROp_FloatLit: + case kIROp_IntLit: { - code = combineHash(code, Slang::getHashCode(args[aa].get())); + SLANG_COMPILE_TIME_ASSERT(sizeof(IRFloatingPointValue) == sizeof(IRIntegerValue)); + // ... we can just compare as bits + return value.intVal == rhs->value.intVal; } - return code; - } - - UnownedStringSlice IRConstant::getStringSlice() - { - assert(getOp() == kIROp_StringLit || getOp() == kIROp_BlobLit); - // If the transitory decoration is set, then this is uses the transitoryStringVal for the text storage. - // This is typically used when we are using a transitory IRInst held on the stack (such that it can be looked up in cached), - // that just points to a string elsewhere, and NOT the typical normal style, where the string is held after the instruction in memory. - // - if(findDecorationImpl(kIROp_TransitoryDecoration)) + case kIROp_PtrLit: { - return UnownedStringSlice(value.transitoryStringVal.chars, value.transitoryStringVal.numChars); + return value.ptrVal == rhs->value.ptrVal; } - else + case kIROp_BlobLit: + case kIROp_StringLit: { - return UnownedStringSlice(value.stringVal.chars, value.stringVal.numChars); + return getStringSlice() == rhs->getStringSlice(); } + case kIROp_VoidLit: + { + return true; + } + default: break; } - bool IRConstant::isFinite() const - { - SLANG_ASSERT(getOp() == kIROp_FloatLit); - - // Lets check we can analyze as double, at least in principal - SLANG_COMPILE_TIME_ASSERT(sizeof(IRFloatingPointValue) == sizeof(double)); - // We are in effect going to type pun (yay!), lets make sure they are the same size - SLANG_COMPILE_TIME_ASSERT(sizeof(IRIntegerValue) == sizeof(IRFloatingPointValue)); + SLANG_ASSERT(!"Unhandled type"); + return false; +} - const uint64_t i = uint64_t(value.intVal); - int e = int(i >> 52) & 0x7ff; - return (e != 0x7ff); - } +/// True if constants are equal +bool IRConstant::equal(IRConstant* rhs) +{ + // TODO(JS): Only equal if pointer types are identical (to match how getHashCode works below) + return isValueEqual(rhs) && getFullType() == rhs->getFullType(); +} - IRConstant::FloatKind IRConstant::getFloatKind() const - { - SLANG_ASSERT(getOp() == kIROp_FloatLit); +HashCode IRConstant::getHashCode() +{ + auto code = Slang::getHashCode(getOp()); + code = combineHash(code, Slang::getHashCode(getFullType())); - const uint64_t i = uint64_t(value.intVal); - int e = int(i >> 52) & 0x7ff; - if ( e == 0x7ff) + switch (getOp()) + { + case kIROp_BoolLit: + case kIROp_FloatLit: + case kIROp_IntLit: { - if (i << 12) - { - return FloatKind::Nan; - } - // Sign bit (top bit) will indicate positive or negative nan - return value.intVal < 0 ? FloatKind::NegativeInfinity : FloatKind::PositiveInfinity; + SLANG_COMPILE_TIME_ASSERT(sizeof(IRFloatingPointValue) == sizeof(IRIntegerValue)); + // ... we can just compare as bits + return combineHash(code, Slang::getHashCode(value.intVal)); } - return FloatKind::Finite; - } - - bool IRConstant::isValueEqual(IRConstant* rhs) - { - // If they are literally the same thing.. - if (this == rhs) + case kIROp_PtrLit: { - return true; + return combineHash(code, Slang::getHashCode(value.ptrVal)); } - // Check the type and they are the same op & same type - if (getOp() != rhs->getOp()) + case kIROp_BlobLit: + case kIROp_StringLit: { - return false; + const UnownedStringSlice slice = getStringSlice(); + return combineHash(code, Slang::getHashCode(slice.begin(), slice.getLength())); } - - switch (getOp()) + case kIROp_VoidLit: { - case kIROp_BoolLit: - case kIROp_FloatLit: - case kIROp_IntLit: - { - SLANG_COMPILE_TIME_ASSERT(sizeof(IRFloatingPointValue) == sizeof(IRIntegerValue)); - // ... we can just compare as bits - return value.intVal == rhs->value.intVal; - } - case kIROp_PtrLit: - { - return value.ptrVal == rhs->value.ptrVal; - } - case kIROp_BlobLit: - case kIROp_StringLit: - { - return getStringSlice() == rhs->getStringSlice(); - } - case kIROp_VoidLit: - { - return true; - } - default: break; + return code; + } + default: + { + SLANG_ASSERT(!"Invalid type"); + return 0; } - - SLANG_ASSERT(!"Unhandled type"); - return false; } +} - /// True if constants are equal - bool IRConstant::equal(IRConstant* rhs) +void IRBuilder::setInsertAfter(IRInst* insertAfter) +{ + auto next = insertAfter->getNextInst(); + if (next) { - // TODO(JS): Only equal if pointer types are identical (to match how getHashCode works below) - return isValueEqual(rhs) && getFullType() == rhs->getFullType(); + setInsertBefore(next); } - - HashCode IRConstant::getHashCode() + else { - auto code = Slang::getHashCode(getOp()); - code = combineHash(code, Slang::getHashCode(getFullType())); + setInsertInto(insertAfter->parent); + } +} - switch (getOp()) - { - case kIROp_BoolLit: - case kIROp_FloatLit: - case kIROp_IntLit: - { - SLANG_COMPILE_TIME_ASSERT(sizeof(IRFloatingPointValue) == sizeof(IRIntegerValue)); - // ... we can just compare as bits - return combineHash(code, Slang::getHashCode(value.intVal)); - } - case kIROp_PtrLit: - { - return combineHash(code, Slang::getHashCode(value.ptrVal)); - } - case kIROp_BlobLit: - case kIROp_StringLit: - { - const UnownedStringSlice slice = getStringSlice(); - return combineHash(code, Slang::getHashCode(slice.begin(), slice.getLength())); - } - case kIROp_VoidLit: - { - return code; - } - default: - { - SLANG_ASSERT(!"Invalid type"); - return 0; - } - } +IRConstant* IRBuilder::_findOrEmitConstant(IRConstant& keyInst) +{ + // We now know where we want to insert, but there might + // already be an equivalent instruction in that block. + // + // We will check for such an instruction in a slightly hacky + // way: we will construct a temporary instruction and + // then use it to look up in a cache of instructions. + // The 'fake' instruction is passed in as keyInst. + + IRConstantKey key; + key.inst = &keyInst; + + IRConstant* irValue = nullptr; + if (m_dedupContext->getConstantMap().tryGetValue(key, irValue)) + { + // We found a match, so just use that. + return irValue; } - void IRBuilder::setInsertAfter(IRInst* insertAfter) + // Calculate the minimum object size (ie not including the payload of value) + const size_t prefixSize = SLANG_OFFSET_OF(IRConstant, value); + + switch (keyInst.getOp()) { - auto next = insertAfter->getNextInst(); - if(next) + default: SLANG_UNEXPECTED("missing case for IR constant"); break; + + case kIROp_BoolLit: + case kIROp_IntLit: { - setInsertBefore(next); + const size_t instSize = prefixSize + sizeof(IRIntegerValue); + irValue = static_cast<IRConstant*>( + _createInst(instSize, keyInst.getFullType(), keyInst.getOp())); + irValue->value.intVal = keyInst.value.intVal; + break; } - else + case kIROp_FloatLit: { - setInsertInto(insertAfter->parent); + const size_t instSize = prefixSize + sizeof(IRFloatingPointValue); + irValue = static_cast<IRConstant*>( + _createInst(instSize, keyInst.getFullType(), keyInst.getOp())); + irValue->value.floatVal = keyInst.value.floatVal; + break; } - } - - IRConstant* IRBuilder::_findOrEmitConstant( - IRConstant& keyInst) - { - // We now know where we want to insert, but there might - // already be an equivalent instruction in that block. - // - // We will check for such an instruction in a slightly hacky - // way: we will construct a temporary instruction and - // then use it to look up in a cache of instructions. - // The 'fake' instruction is passed in as keyInst. - - IRConstantKey key; - key.inst = &keyInst; - - IRConstant* irValue = nullptr; - if (m_dedupContext->getConstantMap().tryGetValue(key, irValue)) + case kIROp_PtrLit: { - // We found a match, so just use that. - return irValue; + const size_t instSize = prefixSize + sizeof(void*); + irValue = static_cast<IRConstant*>( + _createInst(instSize, keyInst.getFullType(), keyInst.getOp())); + irValue->value.ptrVal = keyInst.value.ptrVal; + break; } - - // Calculate the minimum object size (ie not including the payload of value) - const size_t prefixSize = SLANG_OFFSET_OF(IRConstant, value); - - switch (keyInst.getOp()) + case kIROp_VoidLit: { - default: - SLANG_UNEXPECTED("missing case for IR constant"); + const size_t instSize = prefixSize + sizeof(void*); + irValue = static_cast<IRConstant*>( + _createInst(instSize, keyInst.getFullType(), keyInst.getOp())); + irValue->value.ptrVal = keyInst.value.ptrVal; break; + } + case kIROp_BlobLit: + case kIROp_StringLit: + { + const UnownedStringSlice slice = keyInst.getStringSlice(); - case kIROp_BoolLit: - case kIROp_IntLit: - { - const size_t instSize = prefixSize + sizeof(IRIntegerValue); - irValue = static_cast<IRConstant*>(_createInst(instSize, keyInst.getFullType(), keyInst.getOp())); - irValue->value.intVal = keyInst.value.intVal; - break; - } - case kIROp_FloatLit: - { - const size_t instSize = prefixSize + sizeof(IRFloatingPointValue); - irValue = static_cast<IRConstant*>(_createInst(instSize, keyInst.getFullType(), keyInst.getOp())); - irValue->value.floatVal = keyInst.value.floatVal; - break; - } - case kIROp_PtrLit: - { - const size_t instSize = prefixSize + sizeof(void*); - irValue = static_cast<IRConstant*>(_createInst(instSize, keyInst.getFullType(), keyInst.getOp())); - irValue->value.ptrVal = keyInst.value.ptrVal; - break; - } - case kIROp_VoidLit: - { - const size_t instSize = prefixSize + sizeof(void*); - irValue = static_cast<IRConstant*>( - _createInst(instSize, keyInst.getFullType(), keyInst.getOp())); - irValue->value.ptrVal = keyInst.value.ptrVal; - break; - } - case kIROp_BlobLit: - case kIROp_StringLit: - { - const UnownedStringSlice slice = keyInst.getStringSlice(); - - const size_t sliceSize = slice.getLength(); - const size_t instSize = prefixSize + offsetof(IRConstant::StringValue, chars) + sliceSize; + const size_t sliceSize = slice.getLength(); + const size_t instSize = + prefixSize + offsetof(IRConstant::StringValue, chars) + sliceSize; - irValue = static_cast<IRConstant*>(_createInst(instSize, keyInst.getFullType(), keyInst.getOp())); + irValue = static_cast<IRConstant*>( + _createInst(instSize, keyInst.getFullType(), keyInst.getOp())); - IRConstant::StringValue& dstString = irValue->value.stringVal; + IRConstant::StringValue& dstString = irValue->value.stringVal; - dstString.numChars = uint32_t(sliceSize); - // Turn into pointer to avoid warning of array overrun - char* dstChars = dstString.chars; - // Copy the chars - memcpy(dstChars, slice.begin(), sliceSize); + dstString.numChars = uint32_t(sliceSize); + // Turn into pointer to avoid warning of array overrun + char* dstChars = dstString.chars; + // Copy the chars + memcpy(dstChars, slice.begin(), sliceSize); - break; - } + break; } + } - key.inst = irValue; - m_dedupContext->getConstantMap().add(key, irValue); + key.inst = irValue; + m_dedupContext->getConstantMap().add(key, irValue); - addHoistableInst(this, irValue); + addHoistableInst(this, irValue); - return irValue; - } + return irValue; +} - // +// - IRInst* IRBuilder::getBoolValue(bool inValue) - { - IRConstant keyInst; - memset(&keyInst, 0, sizeof(keyInst)); +IRInst* IRBuilder::getBoolValue(bool inValue) +{ + IRConstant keyInst; + memset(&keyInst, 0, sizeof(keyInst)); + keyInst.m_op = kIROp_BoolLit; + keyInst.typeUse.usedValue = getBoolType(); + keyInst.value.intVal = IRIntegerValue(inValue); + return _findOrEmitConstant(keyInst); +} + +IRInst* IRBuilder::getIntValue(IRType* type, IRIntegerValue inValue) +{ + IRConstant keyInst; + memset(&keyInst, 0, sizeof(keyInst)); + keyInst.m_op = kIROp_IntLit; + keyInst.typeUse.usedValue = type; + // Truncate the input value based on `type`. + switch (type->getOp()) + { + case kIROp_Int8Type: keyInst.value.intVal = static_cast<int8_t>(inValue); break; + case kIROp_Int16Type: keyInst.value.intVal = static_cast<int16_t>(inValue); break; + case kIROp_IntType: keyInst.value.intVal = static_cast<int32_t>(inValue); break; + case kIROp_UInt8Type: keyInst.value.intVal = static_cast<uint8_t>(inValue); break; + case kIROp_UInt16Type: keyInst.value.intVal = static_cast<uint16_t>(inValue); break; + case kIROp_BoolType: keyInst.m_op = kIROp_BoolLit; - keyInst.typeUse.usedValue = getBoolType(); - keyInst.value.intVal = IRIntegerValue(inValue); - return _findOrEmitConstant(keyInst); + keyInst.value.intVal = ((inValue != 0) ? 1 : 0); + break; + case kIROp_UIntType: keyInst.value.intVal = static_cast<uint32_t>(inValue); break; + default: keyInst.value.intVal = inValue; break; } + return _findOrEmitConstant(keyInst); +} - IRInst* IRBuilder::getIntValue(IRType* type, IRIntegerValue inValue) +IRInst* IRBuilder::getFloatValue(IRType* type, IRFloatingPointValue inValue) +{ + IRConstant keyInst; + memset(&keyInst, 0, sizeof(keyInst)); + keyInst.m_op = kIROp_FloatLit; + keyInst.typeUse.usedValue = type; + // Truncate the input value based on `type`. + switch (type->getOp()) { - IRConstant keyInst; - memset(&keyInst, 0, sizeof(keyInst)); - keyInst.m_op = kIROp_IntLit; - keyInst.typeUse.usedValue = type; - // Truncate the input value based on `type`. - switch (type->getOp()) - { - case kIROp_Int8Type: - keyInst.value.intVal = static_cast<int8_t>(inValue); - break; - case kIROp_Int16Type: - keyInst.value.intVal = static_cast<int16_t>(inValue); - break; - case kIROp_IntType: - keyInst.value.intVal = static_cast<int32_t>(inValue); - break; - case kIROp_UInt8Type: - keyInst.value.intVal = static_cast<uint8_t>(inValue); - break; - case kIROp_UInt16Type: - keyInst.value.intVal = static_cast<uint16_t>(inValue); - break; - case kIROp_BoolType: - keyInst.m_op = kIROp_BoolLit; - keyInst.value.intVal = ((inValue != 0) ? 1 : 0); - break; - case kIROp_UIntType: - keyInst.value.intVal = static_cast<uint32_t>(inValue); - break; - default: - keyInst.value.intVal = inValue; - break; - } - return _findOrEmitConstant(keyInst); + case kIROp_FloatType: keyInst.value.floatVal = static_cast<float>(inValue); break; + case kIROp_HalfType: keyInst.value.floatVal = HalfToFloat(FloatToHalf((float)inValue)); break; + default: keyInst.value.floatVal = inValue; break; } - IRInst* IRBuilder::getFloatValue(IRType* type, IRFloatingPointValue inValue) - { - IRConstant keyInst; - memset(&keyInst, 0, sizeof(keyInst)); - keyInst.m_op = kIROp_FloatLit; - keyInst.typeUse.usedValue = type; - // Truncate the input value based on `type`. - switch (type->getOp()) - { - case kIROp_FloatType: - keyInst.value.floatVal = static_cast<float>(inValue); - break; - case kIROp_HalfType: - keyInst.value.floatVal = HalfToFloat(FloatToHalf((float)inValue)); - break; - default: - keyInst.value.floatVal = inValue; - break; - } - - return _findOrEmitConstant(keyInst); - } + return _findOrEmitConstant(keyInst); +} - IRStringLit* IRBuilder::getStringValue(const UnownedStringSlice& inSlice) - { - IRConstant keyInst; - memset(&keyInst, 0, sizeof(keyInst)); - - // Mark that this is on the stack... - IRDecoration stackDecoration; - memset(&stackDecoration, 0, sizeof(stackDecoration)); - stackDecoration.m_op = kIROp_TransitoryDecoration; - stackDecoration.insertAtEnd(&keyInst); - - keyInst.m_op = kIROp_StringLit; - keyInst.typeUse.usedValue = getStringType(); - - IRConstant::StringSliceValue& dstSlice = keyInst.value.transitoryStringVal; - dstSlice.chars = const_cast<char*>(inSlice.begin()); - dstSlice.numChars = uint32_t(inSlice.getLength()); +IRStringLit* IRBuilder::getStringValue(const UnownedStringSlice& inSlice) +{ + IRConstant keyInst; + memset(&keyInst, 0, sizeof(keyInst)); - return static_cast<IRStringLit*>(_findOrEmitConstant(keyInst)); - } + // Mark that this is on the stack... + IRDecoration stackDecoration; + memset(&stackDecoration, 0, sizeof(stackDecoration)); + stackDecoration.m_op = kIROp_TransitoryDecoration; + stackDecoration.insertAtEnd(&keyInst); - IRBlobLit* IRBuilder::getBlobValue(ISlangBlob* blob) - { - IRConstant keyInst; - memset(&keyInst, 0, sizeof(keyInst)); + keyInst.m_op = kIROp_StringLit; + keyInst.typeUse.usedValue = getStringType(); - char* buffer = (char*)(getModule()->getMemoryArena().allocate(blob->getBufferSize())); - if (!buffer) - { - return nullptr; - } - memcpy(buffer, blob->getBufferPointer(), blob->getBufferSize()); + IRConstant::StringSliceValue& dstSlice = keyInst.value.transitoryStringVal; + dstSlice.chars = const_cast<char*>(inSlice.begin()); + dstSlice.numChars = uint32_t(inSlice.getLength()); - UnownedStringSlice inSlice(buffer, blob->getBufferSize()); + return static_cast<IRStringLit*>(_findOrEmitConstant(keyInst)); +} - // Mark that this is on the stack... - IRDecoration stackDecoration; - memset(&stackDecoration, 0, sizeof(stackDecoration)); - stackDecoration.m_op = kIROp_TransitoryDecoration; - stackDecoration.insertAtEnd(&keyInst); +IRBlobLit* IRBuilder::getBlobValue(ISlangBlob* blob) +{ + IRConstant keyInst; + memset(&keyInst, 0, sizeof(keyInst)); - keyInst.m_op = kIROp_BlobLit; - keyInst.typeUse.usedValue = nullptr; // not used + char* buffer = (char*)(getModule()->getMemoryArena().allocate(blob->getBufferSize())); + if (!buffer) + { + return nullptr; + } + memcpy(buffer, blob->getBufferPointer(), blob->getBufferSize()); - IRConstant::StringSliceValue& dstSlice = keyInst.value.transitoryStringVal; - dstSlice.chars = const_cast<char*>(inSlice.begin()); - dstSlice.numChars = uint32_t(inSlice.getLength()); + UnownedStringSlice inSlice(buffer, blob->getBufferSize()); - return static_cast<IRBlobLit*>(_findOrEmitConstant(keyInst)); - } + // Mark that this is on the stack... + IRDecoration stackDecoration; + memset(&stackDecoration, 0, sizeof(stackDecoration)); + stackDecoration.m_op = kIROp_TransitoryDecoration; + stackDecoration.insertAtEnd(&keyInst); - IRPtrLit* IRBuilder::_getPtrValue(void* data) - { - auto type = getPtrType(getVoidType()); - IRConstant keyInst; - memset(&keyInst, 0, sizeof(keyInst)); - keyInst.m_op = kIROp_PtrLit; - keyInst.typeUse.usedValue = type; - keyInst.value.ptrVal = data; - return (IRPtrLit*)_findOrEmitConstant(keyInst); - } + keyInst.m_op = kIROp_BlobLit; + keyInst.typeUse.usedValue = nullptr; // not used - IRPtrLit* IRBuilder::getNullPtrValue(IRType* type) - { - IRConstant keyInst; - memset(&keyInst, 0, sizeof(keyInst)); - keyInst.m_op = kIROp_PtrLit; - keyInst.typeUse.usedValue = type; - keyInst.value.ptrVal = nullptr; - return (IRPtrLit*) _findOrEmitConstant(keyInst); - } + IRConstant::StringSliceValue& dstSlice = keyInst.value.transitoryStringVal; + dstSlice.chars = const_cast<char*>(inSlice.begin()); + dstSlice.numChars = uint32_t(inSlice.getLength()); - IRVoidLit* IRBuilder::getVoidValue() - { - IRType* type = getVoidType(); + return static_cast<IRBlobLit*>(_findOrEmitConstant(keyInst)); +} - IRConstant keyInst; - memset(&keyInst, 0, sizeof(keyInst)); - keyInst.m_op = kIROp_VoidLit; - keyInst.typeUse.usedValue = type; - keyInst.value.intVal= 0; - return (IRVoidLit*)_findOrEmitConstant(keyInst); - } +IRPtrLit* IRBuilder::_getPtrValue(void* data) +{ + auto type = getPtrType(getVoidType()); + IRConstant keyInst; + memset(&keyInst, 0, sizeof(keyInst)); + keyInst.m_op = kIROp_PtrLit; + keyInst.typeUse.usedValue = type; + keyInst.value.ptrVal = data; + return (IRPtrLit*)_findOrEmitConstant(keyInst); +} + +IRPtrLit* IRBuilder::getNullPtrValue(IRType* type) +{ + IRConstant keyInst; + memset(&keyInst, 0, sizeof(keyInst)); + keyInst.m_op = kIROp_PtrLit; + keyInst.typeUse.usedValue = type; + keyInst.value.ptrVal = nullptr; + return (IRPtrLit*)_findOrEmitConstant(keyInst); +} + +IRVoidLit* IRBuilder::getVoidValue() +{ + IRType* type = getVoidType(); - IRInst* IRBuilder::getCapabilityValue(CapabilitySet const& caps) - { - IRType* capabilityAtomType = getIntType(); - IRType* capabilitySetType = getCapabilitySetType(); + IRConstant keyInst; + memset(&keyInst, 0, sizeof(keyInst)); + keyInst.m_op = kIROp_VoidLit; + keyInst.typeUse.usedValue = type; + keyInst.value.intVal = 0; + return (IRVoidLit*)_findOrEmitConstant(keyInst); +} - // Not: Our `CapabilitySet` representation consists of a list - // of `CapabilityAtom`s, and by default the list is stored - // "expanded" so that it includes atoms that are transitively - // implied by one another. - // - // For representation in the IR, it is preferable to include - // as few atoms as possible, so that we don't store anything - // redundant in, e.g., serialized modules. - // - // We thus requqest a list of "compacted" atoms which should - // be a minimal list of atoms such that they will produce - // the same `CapabilitySet` when expanded. +IRInst* IRBuilder::getCapabilityValue(CapabilitySet const& caps) +{ + IRType* capabilityAtomType = getIntType(); + IRType* capabilitySetType = getCapabilitySetType(); - auto compactedAtoms = caps.getAtomSets(); - List<IRInst*> conjunctions; - for( auto& atomConjunctionSet : compactedAtoms ) - { - List<IRInst*> args; - for (auto atom : atomConjunctionSet) - args.add(getIntValue(capabilityAtomType, Int(atom))); - auto conjunctionInst = createIntrinsicInst( - capabilitySetType, kIROp_CapabilityConjunction, args.getCount(), args.getBuffer()); - conjunctions.add(conjunctionInst); - } - if (conjunctions.getCount() == 1) - return conjunctions[0]; - return createIntrinsicInst(capabilitySetType, kIROp_CapabilityDisjunction, conjunctions.getCount(), conjunctions.getBuffer()); - } + // Not: Our `CapabilitySet` representation consists of a list + // of `CapabilityAtom`s, and by default the list is stored + // "expanded" so that it includes atoms that are transitively + // implied by one another. + // + // For representation in the IR, it is preferable to include + // as few atoms as possible, so that we don't store anything + // redundant in, e.g., serialized modules. + // + // We thus requqest a list of "compacted" atoms which should + // be a minimal list of atoms such that they will produce + // the same `CapabilitySet` when expanded. - static void canonicalizeInstOperands(IRBuilder& builder, IROp op, ArrayView<IRInst*> operands) + auto compactedAtoms = caps.getAtomSets(); + List<IRInst*> conjunctions; + for (auto& atomConjunctionSet : compactedAtoms) + { + List<IRInst*> args; + for (auto atom : atomConjunctionSet) + args.add(getIntValue(capabilityAtomType, Int(atom))); + auto conjunctionInst = createIntrinsicInst( + capabilitySetType, + kIROp_CapabilityConjunction, + args.getCount(), + args.getBuffer()); + conjunctions.add(conjunctionInst); + } + if (conjunctions.getCount() == 1) + return conjunctions[0]; + return createIntrinsicInst( + capabilitySetType, + kIROp_CapabilityDisjunction, + conjunctions.getCount(), + conjunctions.getBuffer()); +} + +static void canonicalizeInstOperands(IRBuilder& builder, IROp op, ArrayView<IRInst*> operands) +{ + // For Array types, we always want to make sure its element count + // has an int32_t type. We will convert all other int types to int32_t + // to avoid things like float[8] and float[8U] being distinct types. + if (op == kIROp_ArrayType) { - // For Array types, we always want to make sure its element count - // has an int32_t type. We will convert all other int types to int32_t - // to avoid things like float[8] and float[8U] being distinct types. - if (op == kIROp_ArrayType) + if (operands.getCount() < 2) + return; + IRInst* elementCount = operands[1]; + if (auto intLit = as<IRIntLit>(elementCount)) { - if (operands.getCount() < 2) - return; - IRInst* elementCount = operands[1]; - if (auto intLit = as<IRIntLit>(elementCount)) + if (intLit->getDataType()->getOp() != kIROp_IntType) { - if (intLit->getDataType()->getOp() != kIROp_IntType) - { - IRInst* newElementCount = builder.getIntValue(builder.getIntType(), intLit->getValue()); - operands[1] = newElementCount; - } + IRInst* newElementCount = + builder.getIntValue(builder.getIntType(), intLit->getValue()); + operands[1] = newElementCount; } } } +} - IRInst* IRBuilder::_findOrEmitHoistableInst( - IRType* type, - IROp op, - Int fixedArgCount, - IRInst* const* fixedArgs, - Int varArgListCount, - Int const* listArgCounts, - IRInst* const* const* listArgs) +IRInst* IRBuilder::_findOrEmitHoistableInst( + IRType* type, + IROp op, + Int fixedArgCount, + IRInst* const* fixedArgs, + Int varArgListCount, + Int const* listArgCounts, + IRInst* const* const* listArgs) +{ + UInt operandCount = fixedArgCount; + for (Int ii = 0; ii < varArgListCount; ++ii) { - UInt operandCount = fixedArgCount; - for (Int ii = 0; ii < varArgListCount; ++ii) - { - operandCount += listArgCounts[ii]; - } + operandCount += listArgCounts[ii]; + } + + ShortList<IRInst*, 8> canonicalizedOperands; + canonicalizedOperands.setCount(fixedArgCount); + for (Index i = 0; i < fixedArgCount; i++) + canonicalizedOperands[i] = fixedArgs[i]; - ShortList<IRInst*, 8> canonicalizedOperands; - canonicalizedOperands.setCount(fixedArgCount); - for (Index i = 0; i < fixedArgCount; i++) - canonicalizedOperands[i] = fixedArgs[i]; + canonicalizeInstOperands(*this, op, canonicalizedOperands.getArrayView().arrayView); - canonicalizeInstOperands(*this, op, canonicalizedOperands.getArrayView().arrayView); + auto& memoryArena = getModule()->getMemoryArena(); + void* cursor = memoryArena.getCursor(); - auto& memoryArena = getModule()->getMemoryArena(); - void* cursor = memoryArena.getCursor(); + // We are going to create a 'dummy' instruction on the memoryArena + // which can be used as a key for lookup, so see if we + // already have an equivalent instruction available to use. + size_t keySize = sizeof(IRInst) + operandCount * sizeof(IRUse); + IRInst* inst = (IRInst*)memoryArena.allocateAndZero(keySize); - // We are going to create a 'dummy' instruction on the memoryArena - // which can be used as a key for lookup, so see if we - // already have an equivalent instruction available to use. - size_t keySize = sizeof(IRInst) + operandCount * sizeof(IRUse); - IRInst* inst = (IRInst*) memoryArena.allocateAndZero(keySize); - - void* endCursor = memoryArena.getCursor(); - // Mark as 'unused' cos it is unused on release builds. - SLANG_UNUSED(endCursor); + void* endCursor = memoryArena.getCursor(); + // Mark as 'unused' cos it is unused on release builds. + SLANG_UNUSED(endCursor); - new(inst) IRInst(); + new (inst) IRInst(); #if SLANG_ENABLE_IR_BREAK_ALLOC - inst->_debugUID = _debugGetAndIncreaseInstCounter(); + inst->_debugUID = _debugGetAndIncreaseInstCounter(); #endif - inst->m_op = op; - inst->typeUse.usedValue = type; - inst->operandCount = (uint32_t) operandCount; + inst->m_op = op; + inst->typeUse.usedValue = type; + inst->operandCount = (uint32_t)operandCount; - // Don't link up as we may free (if we already have this key) + // Don't link up as we may free (if we already have this key) + { + IRUse* operand = inst->getOperands(); + for (Int ii = 0; ii < fixedArgCount; ++ii) { - IRUse* operand = inst->getOperands(); - for (Int ii = 0; ii < fixedArgCount; ++ii) + auto arg = canonicalizedOperands[ii]; + m_dedupContext->getInstReplacementMap().tryGetValue(arg, arg); + operand->usedValue = arg; + operand++; + } + for (Int ii = 0; ii < varArgListCount; ++ii) + { + UInt listOperandCount = listArgCounts[ii]; + for (UInt jj = 0; jj < listOperandCount; ++jj) { - auto arg = canonicalizedOperands[ii]; + auto arg = listArgs[ii][jj]; m_dedupContext->getInstReplacementMap().tryGetValue(arg, arg); operand->usedValue = arg; operand++; } - for (Int ii = 0; ii < varArgListCount; ++ii) - { - UInt listOperandCount = listArgCounts[ii]; - for (UInt jj = 0; jj < listOperandCount; ++jj) - { - auto arg = listArgs[ii][jj]; - m_dedupContext->getInstReplacementMap().tryGetValue(arg, arg); - operand->usedValue = arg; - operand++; - } - } } + } - // Find or add the key/inst + // Find or add the key/inst + { + IRInstKey key = {inst}; + + IRInst** found = m_dedupContext->getGlobalValueNumberingMap().tryGetValueOrAdd(key, inst); + SLANG_ASSERT(endCursor == memoryArena.getCursor()); + // If it's found, just return, and throw away the instruction + if (found) { - IRInstKey key = { inst }; + memoryArena.rewindToCursor(cursor); - IRInst** found = m_dedupContext->getGlobalValueNumberingMap().tryGetValueOrAdd(key, inst); - SLANG_ASSERT(endCursor == memoryArena.getCursor()); - // If it's found, just return, and throw away the instruction - if (found) + // If the found inst is defined in the same parent as current insert location but + // is located after the insert location, we need to move it to the insert location. + auto foundInst = *found; + if (foundInst->getParent() && foundInst->getParent() == getInsertLoc().getParent() && + getInsertLoc().getMode() == IRInsertLoc::Mode::Before) { - memoryArena.rewindToCursor(cursor); - - // If the found inst is defined in the same parent as current insert location but - // is located after the insert location, we need to move it to the insert location. - auto foundInst = *found; - if (foundInst->getParent() && foundInst->getParent() == getInsertLoc().getParent() && - getInsertLoc().getMode() == IRInsertLoc::Mode::Before) + auto insertLoc = getInsertLoc().getInst(); + bool isAfter = false; + for (auto cur = insertLoc->next; cur; cur = cur->next) { - auto insertLoc = getInsertLoc().getInst(); - bool isAfter = false; - for (auto cur = insertLoc->next; cur; cur = cur->next) + if (cur == foundInst) { - if (cur == foundInst) - { - isAfter = true; - break; - } + isAfter = true; + break; } - if (isAfter) - foundInst->insertBefore(insertLoc); } - return *found; + if (isAfter) + foundInst->insertBefore(insertLoc); } + return *found; } + } - // Make the lookup 'inst' instruction into 'proper' instruction. Equivalent to - // IRInst* inst = createInstImpl<IRInst>(builder, op, type, 0, nullptr, operandListCount, listOperandCounts, listOperands); + // Make the lookup 'inst' instruction into 'proper' instruction. Equivalent to + // IRInst* inst = createInstImpl<IRInst>(builder, op, type, 0, nullptr, operandListCount, + // listOperandCounts, listOperands); + { + if (type) { - if (type) - { - inst->typeUse.usedValue = nullptr; - inst->typeUse.init(inst, type); - } - - _maybeSetSourceLoc(inst); - - IRUse*const operands = inst->getOperands(); - for (UInt i = 0; i < operandCount; ++i) - { - IRUse& operand = operands[i]; - auto value = operand.usedValue; - - operand.usedValue = nullptr; - operand.init(inst, value); - } + inst->typeUse.usedValue = nullptr; + inst->typeUse.init(inst, type); } - addHoistableInst(this, inst); + _maybeSetSourceLoc(inst); - return inst; - } + IRUse* const operands = inst->getOperands(); + for (UInt i = 0; i < operandCount; ++i) + { + IRUse& operand = operands[i]; + auto value = operand.usedValue; - IRType* IRBuilder::getType( - IROp op, - UInt operandCount, - IRInst* const* operands) - { - return (IRType*)createIntrinsicInst( - nullptr, - op, - operandCount, - operands); + operand.usedValue = nullptr; + operand.init(inst, value); + } } - IRType* IRBuilder::getType( - IROp op) - { - return getType(op, 0, nullptr); - } + addHoistableInst(this, inst); - IRType* IRBuilder::getType( - IROp op, - IRInst* operand0) - { - return getType(op, 1, &operand0); - } + return inst; +} - IRBasicType* IRBuilder::getBasicType(BaseType baseType) - { - return (IRBasicType*)getType( - IROp((UInt)kIROp_FirstBasicType + (UInt)baseType)); - } +IRType* IRBuilder::getType(IROp op, UInt operandCount, IRInst* const* operands) +{ + return (IRType*)createIntrinsicInst(nullptr, op, operandCount, operands); +} - IRBasicType* IRBuilder::getVoidType() - { - return (IRVoidType*)getType(kIROp_VoidType); - } +IRType* IRBuilder::getType(IROp op) +{ + return getType(op, 0, nullptr); +} - IRBasicType* IRBuilder::getBoolType() - { - return (IRBoolType*)getType(kIROp_BoolType); - } +IRType* IRBuilder::getType(IROp op, IRInst* operand0) +{ + return getType(op, 1, &operand0); +} - IRBasicType* IRBuilder::getIntType() - { - return (IRBasicType*)getType(kIROp_IntType); - } +IRBasicType* IRBuilder::getBasicType(BaseType baseType) +{ + return (IRBasicType*)getType(IROp((UInt)kIROp_FirstBasicType + (UInt)baseType)); +} - IRBasicType* IRBuilder::getInt64Type() - { - return (IRBasicType*)getType(kIROp_Int64Type); - } +IRBasicType* IRBuilder::getVoidType() +{ + return (IRVoidType*)getType(kIROp_VoidType); +} - IRBasicType* IRBuilder::getUIntType() - { - return (IRBasicType*)getType(kIROp_UIntType); - } +IRBasicType* IRBuilder::getBoolType() +{ + return (IRBoolType*)getType(kIROp_BoolType); +} - IRBasicType* IRBuilder::getUInt64Type() - { - return (IRBasicType*)getType(kIROp_UInt64Type); - } +IRBasicType* IRBuilder::getIntType() +{ + return (IRBasicType*)getType(kIROp_IntType); +} - IRBasicType* IRBuilder::getUInt16Type() - { - return (IRBasicType*)getType(kIROp_UInt16Type); - } +IRBasicType* IRBuilder::getInt64Type() +{ + return (IRBasicType*)getType(kIROp_Int64Type); +} - IRBasicType* IRBuilder::getUInt8Type() - { - return (IRBasicType*)getType(kIROp_UInt8Type); - } +IRBasicType* IRBuilder::getUIntType() +{ + return (IRBasicType*)getType(kIROp_UIntType); +} - IRBasicType* IRBuilder::getCharType() - { - return (IRBasicType*)getType(kIROp_CharType); - } +IRBasicType* IRBuilder::getUInt64Type() +{ + return (IRBasicType*)getType(kIROp_UInt64Type); +} - IRStringType* IRBuilder::getStringType() - { - return (IRStringType*)getType(kIROp_StringType); - } +IRBasicType* IRBuilder::getUInt16Type() +{ + return (IRBasicType*)getType(kIROp_UInt16Type); +} - IRNativeStringType* IRBuilder::getNativeStringType() - { - return (IRNativeStringType*)getType(kIROp_NativeStringType); - } +IRBasicType* IRBuilder::getUInt8Type() +{ + return (IRBasicType*)getType(kIROp_UInt8Type); +} - IRNativePtrType* IRBuilder::getNativePtrType(IRType* valueType) - { - return (IRNativePtrType*)getType(kIROp_NativePtrType, (IRInst*)valueType); - } +IRBasicType* IRBuilder::getCharType() +{ + return (IRBasicType*)getType(kIROp_CharType); +} +IRStringType* IRBuilder::getStringType() +{ + return (IRStringType*)getType(kIROp_StringType); +} - IRType* IRBuilder::getCapabilitySetType() - { - return getType(kIROp_CapabilitySetType); - } +IRNativeStringType* IRBuilder::getNativeStringType() +{ + return (IRNativeStringType*)getType(kIROp_NativeStringType); +} - IRDynamicType* IRBuilder::getDynamicType() { return (IRDynamicType*)getType(kIROp_DynamicType); } +IRNativePtrType* IRBuilder::getNativePtrType(IRType* valueType) +{ + return (IRNativePtrType*)getType(kIROp_NativePtrType, (IRInst*)valueType); +} - IRTargetTupleType* IRBuilder::getTargetTupleType(UInt count, IRType* const* types) - { - return (IRTargetTupleType*)getType(kIROp_TargetTupleType, count, (IRInst* const*)types); - } - IRAssociatedType* IRBuilder::getAssociatedType(ArrayView<IRInterfaceType*> constraintTypes) - { - return (IRAssociatedType*)getType(kIROp_AssociatedType, - constraintTypes.getCount(), - (IRInst**)constraintTypes.getBuffer()); - } +IRType* IRBuilder::getCapabilitySetType() +{ + return getType(kIROp_CapabilitySetType); +} - IRThisType* IRBuilder::getThisType(IRType* interfaceType) - { - return (IRThisType*)getType(kIROp_ThisType, interfaceType); - } +IRDynamicType* IRBuilder::getDynamicType() +{ + return (IRDynamicType*)getType(kIROp_DynamicType); +} - IRRawPointerType* IRBuilder::getRawPointerType() - { - return (IRRawPointerType*)getType(kIROp_RawPointerType); - } +IRTargetTupleType* IRBuilder::getTargetTupleType(UInt count, IRType* const* types) +{ + return (IRTargetTupleType*)getType(kIROp_TargetTupleType, count, (IRInst* const*)types); +} - IRRTTIPointerType* IRBuilder::getRTTIPointerType(IRInst* rttiPtr) - { - return (IRRTTIPointerType*)getType(kIROp_RTTIPointerType, rttiPtr); - } +IRAssociatedType* IRBuilder::getAssociatedType(ArrayView<IRInterfaceType*> constraintTypes) +{ + return (IRAssociatedType*)getType( + kIROp_AssociatedType, + constraintTypes.getCount(), + (IRInst**)constraintTypes.getBuffer()); +} - IRRTTIType* IRBuilder::getRTTIType() - { - return (IRRTTIType*)getType(kIROp_RTTIType); - } +IRThisType* IRBuilder::getThisType(IRType* interfaceType) +{ + return (IRThisType*)getType(kIROp_ThisType, interfaceType); +} - IRRTTIHandleType* IRBuilder::getRTTIHandleType() - { - return (IRRTTIHandleType*)getType(kIROp_RTTIHandleType); - } +IRRawPointerType* IRBuilder::getRawPointerType() +{ + return (IRRawPointerType*)getType(kIROp_RawPointerType); +} - IRAnyValueType* IRBuilder::getAnyValueType(IRIntegerValue size) - { - return (IRAnyValueType*)getType(kIROp_AnyValueType, - getIntValue(getUIntType(), size)); - } +IRRTTIPointerType* IRBuilder::getRTTIPointerType(IRInst* rttiPtr) +{ + return (IRRTTIPointerType*)getType(kIROp_RTTIPointerType, rttiPtr); +} - IRAnyValueType* IRBuilder::getAnyValueType(IRInst* size) - { - return (IRAnyValueType*)getType(kIROp_AnyValueType, size); - } +IRRTTIType* IRBuilder::getRTTIType() +{ + return (IRRTTIType*)getType(kIROp_RTTIType); +} - IRTupleType* IRBuilder::getTupleType(UInt count, IRType* const* types) - { - return (IRTupleType*)getType(kIROp_TupleType, count, (IRInst*const*)types); - } +IRRTTIHandleType* IRBuilder::getRTTIHandleType() +{ + return (IRRTTIHandleType*)getType(kIROp_RTTIHandleType); +} - IRTupleType* IRBuilder::getTupleType(IRType* type0, IRType* type1) - { - IRType* operands[] = { type0, type1 }; - return getTupleType(2, operands); - } +IRAnyValueType* IRBuilder::getAnyValueType(IRIntegerValue size) +{ + return (IRAnyValueType*)getType(kIROp_AnyValueType, getIntValue(getUIntType(), size)); +} - IRTupleType* IRBuilder::getTupleType(IRType* type0, IRType* type1, IRType* type2) - { - IRType* operands[] = { type0, type1, type2 }; - return getTupleType(3, operands); - } +IRAnyValueType* IRBuilder::getAnyValueType(IRInst* size) +{ + return (IRAnyValueType*)getType(kIROp_AnyValueType, size); +} - IRTupleType* IRBuilder::getTupleType(IRType* type0, IRType* type1, IRType* type2, IRType* type3) - { - IRType* operands[] = { type0, type1, type2, type3 }; - return getTupleType(SLANG_COUNT_OF(operands), operands); - } +IRTupleType* IRBuilder::getTupleType(UInt count, IRType* const* types) +{ + return (IRTupleType*)getType(kIROp_TupleType, count, (IRInst* const*)types); +} - IRTypePack* IRBuilder::getTypePack(UInt count, IRType* const* types) - { - return (IRTypePack*)getType(kIROp_TypePack, count, (IRInst* const*)types); - } +IRTupleType* IRBuilder::getTupleType(IRType* type0, IRType* type1) +{ + IRType* operands[] = {type0, type1}; + return getTupleType(2, operands); +} - IRExpandType* IRBuilder::getExpandTypeOrVal(IRType* type, IRInst* pattern, ArrayView<IRInst*> capture) - { - ShortList<IRInst*> args; - args.add(pattern); - args.addRange(capture); - return (IRExpandType*)emitIntrinsicInst(type, kIROp_ExpandTypeOrVal, args.getCount(), args.getArrayView().getBuffer()); - } +IRTupleType* IRBuilder::getTupleType(IRType* type0, IRType* type1, IRType* type2) +{ + IRType* operands[] = {type0, type1, type2}; + return getTupleType(3, operands); +} - IRResultType* IRBuilder::getResultType(IRType* valueType, IRType* errorType) - { - IRInst* operands[] = {valueType, errorType}; - return (IRResultType*)getType(kIROp_ResultType, 2, operands); - } +IRTupleType* IRBuilder::getTupleType(IRType* type0, IRType* type1, IRType* type2, IRType* type3) +{ + IRType* operands[] = {type0, type1, type2, type3}; + return getTupleType(SLANG_COUNT_OF(operands), operands); +} - IROptionalType* IRBuilder::getOptionalType(IRType* valueType) - { - return (IROptionalType*)getType(kIROp_OptionalType, valueType); - } +IRTypePack* IRBuilder::getTypePack(UInt count, IRType* const* types) +{ + return (IRTypePack*)getType(kIROp_TypePack, count, (IRInst* const*)types); +} - IRBasicBlockType* IRBuilder::getBasicBlockType() - { - return (IRBasicBlockType*)getType(kIROp_BasicBlockType); - } +IRExpandType* IRBuilder::getExpandTypeOrVal( + IRType* type, + IRInst* pattern, + ArrayView<IRInst*> capture) +{ + ShortList<IRInst*> args; + args.add(pattern); + args.addRange(capture); + return (IRExpandType*)emitIntrinsicInst( + type, + kIROp_ExpandTypeOrVal, + args.getCount(), + args.getArrayView().getBuffer()); +} + +IRResultType* IRBuilder::getResultType(IRType* valueType, IRType* errorType) +{ + IRInst* operands[] = {valueType, errorType}; + return (IRResultType*)getType(kIROp_ResultType, 2, operands); +} - IRTypeKind* IRBuilder::getTypeKind() - { - return (IRTypeKind*)getType(kIROp_TypeKind); - } +IROptionalType* IRBuilder::getOptionalType(IRType* valueType) +{ + return (IROptionalType*)getType(kIROp_OptionalType, valueType); +} - IRGenericKind* IRBuilder::getGenericKind() - { - return (IRGenericKind*)getType(kIROp_GenericKind); - } +IRBasicBlockType* IRBuilder::getBasicBlockType() +{ + return (IRBasicBlockType*)getType(kIROp_BasicBlockType); +} - IRPtrType* IRBuilder::getPtrType(IRType* valueType) - { - return (IRPtrType*) getPtrType(kIROp_PtrType, valueType); - } +IRTypeKind* IRBuilder::getTypeKind() +{ + return (IRTypeKind*)getType(kIROp_TypeKind); +} - IROutType* IRBuilder::getOutType(IRType* valueType) - { - return (IROutType*) getPtrType(kIROp_OutType, valueType); - } +IRGenericKind* IRBuilder::getGenericKind() +{ + return (IRGenericKind*)getType(kIROp_GenericKind); +} - IRInOutType* IRBuilder::getInOutType(IRType* valueType) - { - return (IRInOutType*) getPtrType(kIROp_InOutType, valueType); - } +IRPtrType* IRBuilder::getPtrType(IRType* valueType) +{ + return (IRPtrType*)getPtrType(kIROp_PtrType, valueType); +} - IRRefType* IRBuilder::getRefType(IRType* valueType, AddressSpace addrSpace) - { - return (IRRefType*) getPtrType(kIROp_RefType, valueType, addrSpace); - } +IROutType* IRBuilder::getOutType(IRType* valueType) +{ + return (IROutType*)getPtrType(kIROp_OutType, valueType); +} - IRConstRefType* IRBuilder::getConstRefType(IRType* valueType) - { - return (IRConstRefType*)getPtrType(kIROp_ConstRefType, valueType); - } +IRInOutType* IRBuilder::getInOutType(IRType* valueType) +{ + return (IRInOutType*)getPtrType(kIROp_InOutType, valueType); +} - IRSPIRVLiteralType* IRBuilder::getSPIRVLiteralType(IRType* type) - { - IRInst* operands[] = { type }; - return (IRSPIRVLiteralType*)getType(kIROp_SPIRVLiteralType, 1, operands); - } +IRRefType* IRBuilder::getRefType(IRType* valueType, AddressSpace addrSpace) +{ + return (IRRefType*)getPtrType(kIROp_RefType, valueType, addrSpace); +} - IRPtrTypeBase* IRBuilder::getPtrType(IROp op, IRType* valueType) - { - IRInst* operands[] = { valueType }; - return (IRPtrTypeBase*) getType( - op, - 1, - operands); - } +IRConstRefType* IRBuilder::getConstRefType(IRType* valueType) +{ + return (IRConstRefType*)getPtrType(kIROp_ConstRefType, valueType); +} - IRPtrTypeBase* IRBuilder::getPtrTypeWithAddressSpace(IRType* valueType, IRPtrTypeBase* ptrWithAddrSpace) - { - if (ptrWithAddrSpace->hasAddressSpace()) - return (IRPtrTypeBase*)getPtrType(ptrWithAddrSpace->getOp(), valueType, ptrWithAddrSpace->getAddressSpace()); - return (IRPtrTypeBase*)getPtrType(ptrWithAddrSpace->getOp(), valueType); - } +IRSPIRVLiteralType* IRBuilder::getSPIRVLiteralType(IRType* type) +{ + IRInst* operands[] = {type}; + return (IRSPIRVLiteralType*)getType(kIROp_SPIRVLiteralType, 1, operands); +} - IRPtrType* IRBuilder::getPtrType(IROp op, IRType* valueType, AddressSpace addressSpace) - { - return (IRPtrType*)getPtrType(op, valueType, getIntValue(getUInt64Type(), static_cast<IRIntegerValue>(addressSpace))); - } +IRPtrTypeBase* IRBuilder::getPtrType(IROp op, IRType* valueType) +{ + IRInst* operands[] = {valueType}; + return (IRPtrTypeBase*)getType(op, 1, operands); +} - IRPtrType* IRBuilder::getPtrType(IROp op, IRType* valueType, IRInst* addressSpace) - { - IRInst* operands[] = { valueType, addressSpace }; - return (IRPtrType*)getType(op, addressSpace ? 2 : 1, operands); - } +IRPtrTypeBase* IRBuilder::getPtrTypeWithAddressSpace( + IRType* valueType, + IRPtrTypeBase* ptrWithAddrSpace) +{ + if (ptrWithAddrSpace->hasAddressSpace()) + return (IRPtrTypeBase*) + getPtrType(ptrWithAddrSpace->getOp(), valueType, ptrWithAddrSpace->getAddressSpace()); + return (IRPtrTypeBase*)getPtrType(ptrWithAddrSpace->getOp(), valueType); +} - IRTextureTypeBase* IRBuilder::getTextureType(IRType* elementType, IRInst* shape, IRInst* isArray, IRInst* isMS, IRInst* sampleCount, IRInst* access, IRInst* isShadow, IRInst* isCombined, IRInst* format) - { - IRInst* args[] = {(IRInst*)elementType, shape, isArray, isMS, sampleCount, access, isShadow, isCombined, format}; - return as<IRTextureTypeBase>(emitIntrinsicInst(getTypeKind(), kIROp_TextureType, (UInt)(sizeof(args)/sizeof(IRInst*)), args)); - } +IRPtrType* IRBuilder::getPtrType(IROp op, IRType* valueType, AddressSpace addressSpace) +{ + return (IRPtrType*)getPtrType( + op, + valueType, + getIntValue(getUInt64Type(), static_cast<IRIntegerValue>(addressSpace))); +} - IRComPtrType* IRBuilder::getComPtrType(IRType* valueType) - { - return (IRComPtrType*)getType(kIROp_ComPtrType, valueType); - } +IRPtrType* IRBuilder::getPtrType(IROp op, IRType* valueType, IRInst* addressSpace) +{ + IRInst* operands[] = {valueType, addressSpace}; + return (IRPtrType*)getType(op, addressSpace ? 2 : 1, operands); +} + +IRTextureTypeBase* IRBuilder::getTextureType( + IRType* elementType, + IRInst* shape, + IRInst* isArray, + IRInst* isMS, + IRInst* sampleCount, + IRInst* access, + IRInst* isShadow, + IRInst* isCombined, + IRInst* format) +{ + IRInst* args[] = { + (IRInst*)elementType, + shape, + isArray, + isMS, + sampleCount, + access, + isShadow, + isCombined, + format}; + return as<IRTextureTypeBase>(emitIntrinsicInst( + getTypeKind(), + kIROp_TextureType, + (UInt)(sizeof(args) / sizeof(IRInst*)), + args)); +} + +IRComPtrType* IRBuilder::getComPtrType(IRType* valueType) +{ + return (IRComPtrType*)getType(kIROp_ComPtrType, valueType); +} - IRArrayTypeBase* IRBuilder::getArrayTypeBase( - IROp op, - IRType* elementType, - IRInst* elementCount) - { - IRInst* operands[] = { elementType, elementCount }; - return (IRArrayTypeBase*)getType( - op, - op == kIROp_ArrayType ? 2 : 1, - operands); - } +IRArrayTypeBase* IRBuilder::getArrayTypeBase(IROp op, IRType* elementType, IRInst* elementCount) +{ + IRInst* operands[] = {elementType, elementCount}; + return (IRArrayTypeBase*)getType(op, op == kIROp_ArrayType ? 2 : 1, operands); +} - IRArrayType* IRBuilder::getArrayType( - IRType* elementType, - IRInst* elementCount) - { - IRInst* operands[] = { elementType, elementCount }; - return (IRArrayType*)getType( - kIROp_ArrayType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRArrayType* IRBuilder::getArrayType(IRType* elementType, IRInst* elementCount) +{ + IRInst* operands[] = {elementType, elementCount}; + return (IRArrayType*)getType(kIROp_ArrayType, sizeof(operands) / sizeof(operands[0]), operands); +} - IRUnsizedArrayType* IRBuilder::getUnsizedArrayType( - IRType* elementType) - { - IRInst* operands[] = { elementType }; - return (IRUnsizedArrayType*)getType( - kIROp_UnsizedArrayType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRUnsizedArrayType* IRBuilder::getUnsizedArrayType(IRType* elementType) +{ + IRInst* operands[] = {elementType}; + return (IRUnsizedArrayType*) + getType(kIROp_UnsizedArrayType, sizeof(operands) / sizeof(operands[0]), operands); +} - IRArrayType* IRBuilder::getArrayType( - IRType* elementType, - IRInst* elementCount, - IRInst* stride) - { - IRInst* operands[] = { elementType, elementCount, stride }; - return (IRArrayType*)getType( - kIROp_ArrayType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRArrayType* IRBuilder::getArrayType(IRType* elementType, IRInst* elementCount, IRInst* stride) +{ + IRInst* operands[] = {elementType, elementCount, stride}; + return (IRArrayType*)getType(kIROp_ArrayType, sizeof(operands) / sizeof(operands[0]), operands); +} - IRUnsizedArrayType* IRBuilder::getUnsizedArrayType( - IRType* elementType, - IRInst* stride) - { - IRInst* operands[] = { elementType, stride }; - return (IRUnsizedArrayType*)getType( - kIROp_UnsizedArrayType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRUnsizedArrayType* IRBuilder::getUnsizedArrayType(IRType* elementType, IRInst* stride) +{ + IRInst* operands[] = {elementType, stride}; + return (IRUnsizedArrayType*) + getType(kIROp_UnsizedArrayType, sizeof(operands) / sizeof(operands[0]), operands); +} - IRVectorType* IRBuilder::getVectorType( - IRType* elementType, - IRInst* elementCount) - { - IRInst* operands[] = { elementType, elementCount }; - return (IRVectorType*)getType( - kIROp_VectorType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRVectorType* IRBuilder::getVectorType(IRType* elementType, IRInst* elementCount) +{ + IRInst* operands[] = {elementType, elementCount}; + return ( + IRVectorType*)getType(kIROp_VectorType, sizeof(operands) / sizeof(operands[0]), operands); +} - IRVectorType* IRBuilder::getVectorType( - IRType* elementType, - IRIntegerValue elementCount) - { - return getVectorType(elementType, getIntValue(getIntType(), elementCount)); - } +IRVectorType* IRBuilder::getVectorType(IRType* elementType, IRIntegerValue elementCount) +{ + return getVectorType(elementType, getIntValue(getIntType(), elementCount)); +} + +IRMatrixType* IRBuilder::getMatrixType( + IRType* elementType, + IRInst* rowCount, + IRInst* columnCount, + IRInst* layout) +{ + IRInst* operands[] = {elementType, rowCount, columnCount, layout}; + return ( + IRMatrixType*)getType(kIROp_MatrixType, sizeof(operands) / sizeof(operands[0]), operands); +} - IRMatrixType* IRBuilder::getMatrixType( - IRType* elementType, - IRInst* rowCount, - IRInst* columnCount, - IRInst* layout) - { - IRInst* operands[] = { elementType, rowCount, columnCount, layout }; - return (IRMatrixType*)getType( - kIROp_MatrixType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRArrayListType* IRBuilder::getArrayListType(IRType* elementType) +{ + return (IRArrayListType*)getType(kIROp_ArrayListType, 1, (IRInst**)&elementType); +} - IRArrayListType* IRBuilder::getArrayListType(IRType* elementType) - { - return (IRArrayListType*)getType( - kIROp_ArrayListType, - 1, - (IRInst**)&elementType); - } +IRTensorViewType* IRBuilder::getTensorViewType(IRType* elementType) +{ + return (IRTensorViewType*)getType(kIROp_TensorViewType, 1, (IRInst**)&elementType); +} - IRTensorViewType* IRBuilder::getTensorViewType(IRType* elementType) - { - return (IRTensorViewType*)getType( - kIROp_TensorViewType, - 1, - (IRInst**)&elementType); - } +IRTorchTensorType* IRBuilder::getTorchTensorType(IRType* elementType) +{ + return (IRTorchTensorType*)getType(kIROp_TorchTensorType, 1, (IRInst**)&elementType); +} - IRTorchTensorType* IRBuilder::getTorchTensorType(IRType* elementType) - { - return (IRTorchTensorType*)getType(kIROp_TorchTensorType, 1, (IRInst**)&elementType); - } +IRDifferentialPairType* IRBuilder::getDifferentialPairType(IRType* valueType, IRInst* witnessTable) +{ + IRInst* operands[] = {valueType, witnessTable}; + return (IRDifferentialPairType*) + getType(kIROp_DifferentialPairType, sizeof(operands) / sizeof(operands[0]), operands); +} + +IRDifferentialPtrPairType* IRBuilder::getDifferentialPtrPairType( + IRType* valueType, + IRInst* witnessTable) +{ + IRInst* operands[] = {valueType, witnessTable}; + return (IRDifferentialPtrPairType*) + getType(kIROp_DifferentialPtrPairType, sizeof(operands) / sizeof(operands[0]), operands); +} + +IRDifferentialPairUserCodeType* IRBuilder::getDifferentialPairUserCodeType( + IRType* valueType, + IRInst* witnessTable) +{ + IRInst* operands[] = {valueType, witnessTable}; + return (IRDifferentialPairUserCodeType*)getType( + kIROp_DifferentialPairUserCodeType, + sizeof(operands) / sizeof(operands[0]), + operands); +} + +IRBackwardDiffIntermediateContextType* IRBuilder::getBackwardDiffIntermediateContextType( + IRInst* func) +{ + if (!func) + func = getVoidValue(); + return (IRBackwardDiffIntermediateContextType*) + getType(kIROp_BackwardDiffIntermediateContextType, 1, &func); +} - IRDifferentialPairType* IRBuilder::getDifferentialPairType( - IRType* valueType, - IRInst* witnessTable) - { - IRInst* operands[] = { valueType, witnessTable }; - return (IRDifferentialPairType*)getType( - kIROp_DifferentialPairType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRFuncType* IRBuilder::getFuncType(UInt paramCount, IRType* const* paramTypes, IRType* resultType) +{ + return (IRFuncType*)createIntrinsicInst( + nullptr, + kIROp_FuncType, + resultType, + paramCount, + (IRInst* const*)paramTypes); +} + +IRFuncType* IRBuilder::getFuncType( + UInt paramCount, + IRType* const* paramTypes, + IRType* resultType, + IRAttr* attribute) +{ + UInt counts[3] = {1, paramCount, 1}; + IRInst** lists[3] = {(IRInst**)&resultType, (IRInst**)paramTypes, (IRInst**)&attribute}; + return (IRFuncType*)createIntrinsicInst(nullptr, kIROp_FuncType, 3, counts, lists); +} - IRDifferentialPtrPairType* IRBuilder::getDifferentialPtrPairType( - IRType* valueType, - IRInst* witnessTable) - { - IRInst* operands[] = { valueType, witnessTable }; - return (IRDifferentialPtrPairType*)getType( - kIROp_DifferentialPtrPairType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRWitnessTableType* IRBuilder::getWitnessTableType(IRType* baseType) +{ + return (IRWitnessTableType*) + createIntrinsicInst(nullptr, kIROp_WitnessTableType, 1, (IRInst* const*)&baseType); +} - IRDifferentialPairUserCodeType* IRBuilder::getDifferentialPairUserCodeType( - IRType* valueType, - IRInst* witnessTable) - { - IRInst* operands[] = { valueType, witnessTable }; - return (IRDifferentialPairUserCodeType*)getType( - kIROp_DifferentialPairUserCodeType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRWitnessTableIDType* IRBuilder::getWitnessTableIDType(IRType* baseType) +{ + return (IRWitnessTableIDType*) + createIntrinsicInst(nullptr, kIROp_WitnessTableIDType, 1, (IRInst* const*)&baseType); +} - IRBackwardDiffIntermediateContextType* IRBuilder::getBackwardDiffIntermediateContextType( - IRInst* func) - { - if (!func) - func = getVoidValue(); - return (IRBackwardDiffIntermediateContextType*)getType( - kIROp_BackwardDiffIntermediateContextType, - 1, - &func); - } +IRConstantBufferType* IRBuilder::getConstantBufferType(IRType* elementType) +{ + IRInst* operands[] = {elementType}; + return (IRConstantBufferType*)getType(kIROp_ConstantBufferType, 1, operands); +} - IRFuncType* IRBuilder::getFuncType( - UInt paramCount, - IRType* const* paramTypes, - IRType* resultType) - { - return (IRFuncType*)createIntrinsicInst( - nullptr, - kIROp_FuncType, - resultType, - paramCount, - (IRInst* const*) paramTypes); - } +IRGLSLOutputParameterGroupType* IRBuilder::getGLSLOutputParameterGroupType(IRType* elementType) +{ + IRInst* operands[] = {elementType}; + return ( + IRGLSLOutputParameterGroupType*)getType(kIROp_GLSLOutputParameterGroupType, 1, operands); +} - IRFuncType* IRBuilder::getFuncType( - UInt paramCount, IRType* const* paramTypes, IRType* resultType, IRAttr* attribute) - { - UInt counts[3] = {1, paramCount, 1}; - IRInst** lists[3] = {(IRInst**)&resultType, (IRInst**)paramTypes, (IRInst**)&attribute}; - return (IRFuncType*)createIntrinsicInst(nullptr, kIROp_FuncType, 3, counts, lists); - } +IRConstExprRate* IRBuilder::getConstExprRate() +{ + return (IRConstExprRate*)getType(kIROp_ConstExprRate); +} - IRWitnessTableType* IRBuilder::getWitnessTableType( - IRType* baseType) - { - return (IRWitnessTableType*)createIntrinsicInst( - nullptr, - kIROp_WitnessTableType, - 1, - (IRInst* const*)&baseType); - } +IRGroupSharedRate* IRBuilder::getGroupSharedRate() +{ + return (IRGroupSharedRate*)getType(kIROp_GroupSharedRate); +} +IRActualGlobalRate* IRBuilder::getActualGlobalRate() +{ + return (IRActualGlobalRate*)getType(kIROp_ActualGlobalRate); +} - IRWitnessTableIDType* IRBuilder::getWitnessTableIDType( - IRType* baseType) - { - return (IRWitnessTableIDType*)createIntrinsicInst( - nullptr, - kIROp_WitnessTableIDType, - 1, - (IRInst* const*)&baseType); - } +IRRateQualifiedType* IRBuilder::getRateQualifiedType(IRRate* rate, IRType* dataType) +{ + IRInst* operands[] = {rate, dataType}; + return (IRRateQualifiedType*) + getType(kIROp_RateQualifiedType, sizeof(operands) / sizeof(operands[0]), operands); +} + +IRType* IRBuilder::getBindExistentialsType( + IRInst* baseType, + UInt slotArgCount, + IRInst* const* slotArgs) +{ + if (slotArgCount == 0) + return (IRType*)baseType; - IRConstantBufferType* IRBuilder::getConstantBufferType(IRType* elementType) + // If we are trying to bind an interface type, then + // we will go ahead and simplify the instruction + // away impmediately. + // + if (as<IRInterfaceType>(baseType)) { - IRInst* operands[] = { elementType }; - return (IRConstantBufferType*) getType( - kIROp_ConstantBufferType, - 1, - operands); + if (slotArgCount >= 2) + { + // We are being asked to emit `BindExistentials(someInterface, someConcreteType, ...)` + // so we just want to return `ExistentialBox<someConcreteType>`. + // + auto concreteType = (IRType*)slotArgs[0]; + auto witnessTable = slotArgs[1]; + auto ptrType = getBoundInterfaceType((IRType*)baseType, concreteType, witnessTable); + return ptrType; + } } - IRGLSLOutputParameterGroupType* IRBuilder::getGLSLOutputParameterGroupType(IRType* elementType) - { - IRInst* operands[] = { elementType }; - return (IRGLSLOutputParameterGroupType*) getType( - kIROp_GLSLOutputParameterGroupType, - 1, - operands); - } + return (IRType*)createIntrinsicInst( + getTypeKind(), + kIROp_BindExistentialsType, + baseType, + slotArgCount, + (IRInst* const*)slotArgs); +} - IRConstExprRate* IRBuilder::getConstExprRate() - { - return (IRConstExprRate*)getType(kIROp_ConstExprRate); - } +IRType* IRBuilder::getBindExistentialsType( + IRInst* baseType, + UInt slotArgCount, + IRUse const* slotArgUses) +{ + if (slotArgCount == 0) + return (IRType*)baseType; - IRGroupSharedRate* IRBuilder::getGroupSharedRate() - { - return (IRGroupSharedRate*)getType(kIROp_GroupSharedRate); - } - IRActualGlobalRate* IRBuilder::getActualGlobalRate() + List<IRInst*> slotArgs; + for (UInt ii = 0; ii < slotArgCount; ++ii) { - return (IRActualGlobalRate*)getType(kIROp_ActualGlobalRate); + slotArgs.add(slotArgUses[ii].get()); } + return getBindExistentialsType(baseType, slotArgCount, slotArgs.getBuffer()); +} - IRRateQualifiedType* IRBuilder::getRateQualifiedType( - IRRate* rate, - IRType* dataType) - { - IRInst* operands[] = { rate, dataType }; - return (IRRateQualifiedType*)getType( - kIROp_RateQualifiedType, - sizeof(operands) / sizeof(operands[0]), - operands); - } +IRType* IRBuilder::getBoundInterfaceType( + IRType* interfaceType, + IRType* concreteType, + IRInst* witnessTable) +{ + // Don't wrap an existential box if concreteType is __Dynamic. + if (as<IRDynamicType>(concreteType)) + return interfaceType; - IRType* IRBuilder::getBindExistentialsType( - IRInst* baseType, - UInt slotArgCount, - IRInst* const* slotArgs) - { - if(slotArgCount == 0) - return (IRType*) baseType; + IRInst* operands[] = {interfaceType, concreteType, witnessTable}; + return getType(kIROp_BoundInterfaceType, SLANG_COUNT_OF(operands), operands); +} - // If we are trying to bind an interface type, then - // we will go ahead and simplify the instruction - // away impmediately. - // - if(as<IRInterfaceType>(baseType)) - { - if(slotArgCount >= 2) - { - // We are being asked to emit `BindExistentials(someInterface, someConcreteType, ...)` - // so we just want to return `ExistentialBox<someConcreteType>`. - // - auto concreteType = (IRType*) slotArgs[0]; - auto witnessTable = slotArgs[1]; - auto ptrType = getBoundInterfaceType((IRType*) baseType, concreteType, witnessTable); - return ptrType; - } - } +IRType* IRBuilder::getPseudoPtrType(IRType* concreteType) +{ + IRInst* operands[] = {concreteType}; + return getType(kIROp_PseudoPtrType, SLANG_COUNT_OF(operands), operands); +} - return (IRType*)createIntrinsicInst( - getTypeKind(), - kIROp_BindExistentialsType, - baseType, - slotArgCount, - (IRInst* const*) slotArgs); - } +IRType* IRBuilder::getConjunctionType(UInt typeCount, IRType* const* types) +{ + return getType(kIROp_ConjunctionType, typeCount, (IRInst* const*)types); +} - IRType* IRBuilder::getBindExistentialsType( - IRInst* baseType, - UInt slotArgCount, - IRUse const* slotArgUses) - { - if(slotArgCount == 0) - return (IRType*) baseType; +IRType* IRBuilder::getAttributedType( + IRType* baseType, + UInt attributeCount, + IRAttr* const* attributes) +{ + List<IRInst*> operands; + operands.add(baseType); + for (UInt i = 0; i < attributeCount; ++i) + operands.add(attributes[i]); + return getType(kIROp_AttributedType, operands.getCount(), operands.getBuffer()); +} - List<IRInst*> slotArgs; - for( UInt ii = 0; ii < slotArgCount; ++ii ) - { - slotArgs.add(slotArgUses[ii].get()); - } - return getBindExistentialsType( - baseType, - slotArgCount, - slotArgs.getBuffer()); - } - IRType* IRBuilder::getBoundInterfaceType( - IRType* interfaceType, - IRType* concreteType, - IRInst* witnessTable) +void IRBuilder::setDataType(IRInst* inst, IRType* dataType) +{ + if (auto oldRateQualifiedType = as<IRRateQualifiedType>(inst->getFullType())) { - // Don't wrap an existential box if concreteType is __Dynamic. - if (as<IRDynamicType>(concreteType)) - return interfaceType; + // Construct a new rate-qualified type using the same rate. - IRInst* operands[] = {interfaceType, concreteType, witnessTable}; - return getType(kIROp_BoundInterfaceType, SLANG_COUNT_OF(operands), operands); - } + auto newRateQualifiedType = getRateQualifiedType(oldRateQualifiedType->getRate(), dataType); - IRType* IRBuilder::getPseudoPtrType( - IRType* concreteType) - { - IRInst* operands[] = {concreteType}; - return getType(kIROp_PseudoPtrType, SLANG_COUNT_OF(operands), operands); + inst->setFullType(newRateQualifiedType); } - - IRType* IRBuilder::getConjunctionType( - UInt typeCount, - IRType* const* types) + else { - return getType(kIROp_ConjunctionType, typeCount, (IRInst* const*)types); + // No rate? Just clobber the data type. + inst->setFullType(dataType); } +} - IRType* IRBuilder::getAttributedType( - IRType* baseType, - UInt attributeCount, - IRAttr* const* attributes) - { - List<IRInst*> operands; - operands.add(baseType); - for(UInt i = 0; i < attributeCount; ++i) - operands.add(attributes[i]); - return getType(kIROp_AttributedType, operands.getCount(), operands.getBuffer()); - } - - - void IRBuilder::setDataType(IRInst* inst, IRType* dataType) - { - if (auto oldRateQualifiedType = as<IRRateQualifiedType>(inst->getFullType())) - { - // Construct a new rate-qualified type using the same rate. - - auto newRateQualifiedType = getRateQualifiedType( - oldRateQualifiedType->getRate(), - dataType); +IRInst* IRBuilder::emitGetValueFromBoundInterface(IRType* type, IRInst* boundInterfaceValue) +{ + auto inst = + createInst<IRInst>(this, kIROp_GetValueFromBoundInterface, type, 1, &boundInterfaceValue); + addInst(inst); + return inst; +} - inst->setFullType(newRateQualifiedType); - } - else - { - // No rate? Just clobber the data type. - inst->setFullType(dataType); - } - } - - IRInst* IRBuilder::emitGetValueFromBoundInterface(IRType* type, IRInst* boundInterfaceValue) - { - auto inst = - createInst<IRInst>(this, kIROp_GetValueFromBoundInterface, type, 1, &boundInterfaceValue); - addInst(inst); - return inst; - } +IRUndefined* IRBuilder::emitUndefined(IRType* type) +{ + auto inst = createInst<IRUndefined>(this, kIROp_undefined, type); - IRUndefined* IRBuilder::emitUndefined(IRType* type) - { - auto inst = createInst<IRUndefined>( - this, - kIROp_undefined, - type); + addInst(inst); - addInst(inst); + return inst; +} - return inst; - } +IRInst* IRBuilder::emitByteAddressBufferStore( + IRInst* byteAddressBuffer, + IRInst* offset, + IRInst* value) +{ + IRInst* args[] = {byteAddressBuffer, offset, getIntValue(getUIntType(), 0), value}; + return emitIntrinsicInst(getVoidType(), kIROp_ByteAddressBufferStore, 4, args); +} + +IRInst* IRBuilder::emitByteAddressBufferStore( + IRInst* byteAddressBuffer, + IRInst* offset, + IRInst* alignment, + IRInst* value) +{ + IRInst* args[] = {byteAddressBuffer, offset, alignment, value}; + return emitIntrinsicInst(getVoidType(), kIROp_ByteAddressBufferStore, 4, args); +} - IRInst* IRBuilder::emitByteAddressBufferStore(IRInst* byteAddressBuffer, IRInst* offset, IRInst* value) +IRInst* IRBuilder::emitReinterpret(IRInst* type, IRInst* value) +{ + return emitIntrinsicInst((IRType*)type, kIROp_Reinterpret, 1, &value); +} +IRInst* IRBuilder::emitInOutImplicitCast(IRInst* type, IRInst* value) +{ + return emitIntrinsicInst((IRType*)type, kIROp_InOutImplicitCast, 1, &value); +} +IRInst* IRBuilder::emitOutImplicitCast(IRInst* type, IRInst* value) +{ + return emitIntrinsicInst((IRType*)type, kIROp_OutImplicitCast, 1, &value); +} +IRInst* IRBuilder::emitDebugSource(UnownedStringSlice fileName, UnownedStringSlice source) +{ + IRInst* args[] = {getStringValue(fileName), getStringValue(source)}; + return emitIntrinsicInst(getVoidType(), kIROp_DebugSource, 2, args); +} +IRInst* IRBuilder::emitDebugLine( + IRInst* source, + IRIntegerValue lineStart, + IRIntegerValue lineEnd, + IRIntegerValue colStart, + IRIntegerValue colEnd) +{ + IRInst* args[] = { + source, + getIntValue(getUIntType(), lineStart), + getIntValue(getUIntType(), lineEnd), + getIntValue(getUIntType(), colStart), + getIntValue(getUIntType(), colEnd)}; + return emitIntrinsicInst(getVoidType(), kIROp_DebugLine, 5, args); +} +IRInst* IRBuilder::emitDebugVar( + IRType* type, + IRInst* source, + IRInst* line, + IRInst* col, + IRInst* argIndex) +{ + if (argIndex) { - IRInst* args[] = { byteAddressBuffer, offset, getIntValue(getUIntType(), 0), value}; - return emitIntrinsicInst(getVoidType(), kIROp_ByteAddressBufferStore, 4, args); + IRInst* args[] = {source, line, col, argIndex}; + return emitIntrinsicInst(getPtrType(type), kIROp_DebugVar, 4, args); } - - IRInst* IRBuilder::emitByteAddressBufferStore(IRInst* byteAddressBuffer, IRInst* offset, IRInst* alignment, IRInst* value) + else { - IRInst* args[] = { byteAddressBuffer, offset, alignment, value }; - return emitIntrinsicInst(getVoidType(), kIROp_ByteAddressBufferStore, 4, args); + IRInst* args[] = {source, line, col}; + return emitIntrinsicInst(getPtrType(type), kIROp_DebugVar, 3, args); } +} - IRInst* IRBuilder::emitReinterpret(IRInst* type, IRInst* value) - { - return emitIntrinsicInst((IRType*)type, kIROp_Reinterpret, 1, &value); - } - IRInst* IRBuilder::emitInOutImplicitCast(IRInst* type, IRInst* value) - { - return emitIntrinsicInst((IRType*)type, kIROp_InOutImplicitCast, 1, &value); - } - IRInst* IRBuilder::emitOutImplicitCast(IRInst* type, IRInst* value) - { - return emitIntrinsicInst((IRType*)type, kIROp_OutImplicitCast, 1, &value); - } - IRInst* IRBuilder::emitDebugSource(UnownedStringSlice fileName, UnownedStringSlice source) - { - IRInst* args[] = { getStringValue(fileName), getStringValue(source) }; - return emitIntrinsicInst(getVoidType(), kIROp_DebugSource, 2, args); - } - IRInst* IRBuilder::emitDebugLine(IRInst* source, IRIntegerValue lineStart, IRIntegerValue lineEnd, IRIntegerValue colStart, IRIntegerValue colEnd) - { - IRInst* args[] = - { - source, - getIntValue(getUIntType(), lineStart), - getIntValue(getUIntType(), lineEnd), - getIntValue(getUIntType(), colStart), - getIntValue(getUIntType(), colEnd) - }; - return emitIntrinsicInst(getVoidType(), kIROp_DebugLine, 5, args); - } - IRInst* IRBuilder::emitDebugVar(IRType* type, IRInst* source, IRInst* line, IRInst* col, IRInst* argIndex) - { - if (argIndex) - { - IRInst* args[] = { source, line, col, argIndex }; - return emitIntrinsicInst(getPtrType(type), kIROp_DebugVar, 4, args); - } - else - { - IRInst* args[] = { source, line, col }; - return emitIntrinsicInst(getPtrType(type), kIROp_DebugVar, 3, args); - } - } +IRInst* IRBuilder::emitDebugValue(IRInst* debugVar, IRInst* debugValue) +{ + List<IRInst*> args; + args.add(debugVar); + args.add(debugValue); + return emitIntrinsicInst( + getVoidType(), + kIROp_DebugValue, + (UInt)args.getCount(), + args.getBuffer()); +} + +IRLiveRangeStart* IRBuilder::emitLiveRangeStart(IRInst* referenced) +{ + // This instruction doesn't produce any result, + // so we make it's type void. + auto inst = createInst<IRLiveRangeStart>(this, kIROp_LiveRangeStart, getVoidType(), referenced); - IRInst* IRBuilder::emitDebugValue(IRInst* debugVar, IRInst* debugValue) - { - List<IRInst*> args; - args.add(debugVar); - args.add(debugValue); - return emitIntrinsicInst(getVoidType(), kIROp_DebugValue, (UInt)args.getCount(), args.getBuffer()); - } + addInst(inst); - IRLiveRangeStart* IRBuilder::emitLiveRangeStart(IRInst* referenced) - { - // This instruction doesn't produce any result, - // so we make it's type void. - auto inst = createInst<IRLiveRangeStart>( - this, - kIROp_LiveRangeStart, - getVoidType(), - referenced); - - addInst(inst); + return inst; +} - return inst; - } +IRLiveRangeEnd* IRBuilder::emitLiveRangeEnd(IRInst* referenced) +{ + // This instruction doesn't produce any result, + // so we make it's type void. + auto inst = createInst<IRLiveRangeEnd>(this, kIROp_LiveRangeEnd, getVoidType(), referenced); - IRLiveRangeEnd* IRBuilder::emitLiveRangeEnd(IRInst* referenced) - { - // This instruction doesn't produce any result, - // so we make it's type void. - auto inst = createInst<IRLiveRangeEnd>( - this, - kIROp_LiveRangeEnd, - getVoidType(), - referenced); + addInst(inst); - addInst(inst); + return inst; +} - return inst; - } +IRInst* IRBuilder::emitExtractExistentialValue(IRType* type, IRInst* existentialValue) +{ + auto inst = createInst<IRInst>(this, kIROp_ExtractExistentialValue, type, 1, &existentialValue); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitExtractExistentialValue( - IRType* type, - IRInst* existentialValue) - { - auto inst = createInst<IRInst>( - this, - kIROp_ExtractExistentialValue, - type, - 1, - &existentialValue); - addInst(inst); - return inst; - } +IRType* IRBuilder::emitExtractExistentialType(IRInst* existentialValue) +{ + auto type = getTypeKind(); + auto inst = createInst<IRInst>(this, kIROp_ExtractExistentialType, type, 1, &existentialValue); + addInst(inst); + return (IRType*)inst; +} - IRType* IRBuilder::emitExtractExistentialType( - IRInst* existentialValue) - { - auto type = getTypeKind(); - auto inst = createInst<IRInst>( - this, - kIROp_ExtractExistentialType, - type, - 1, - &existentialValue); - addInst(inst); - return (IRType*) inst; - } +IRInst* IRBuilder::emitExtractExistentialWitnessTable(IRInst* existentialValue) +{ + auto type = getWitnessTableType(existentialValue->getDataType()); + auto inst = + createInst<IRInst>(this, kIROp_ExtractExistentialWitnessTable, type, 1, &existentialValue); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitForwardDifferentiateInst(IRType* type, IRInst* baseFn) +{ + auto inst = createInst<IRForwardDifferentiate>(this, kIROp_ForwardDifferentiate, type, baseFn); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitExtractExistentialWitnessTable( - IRInst* existentialValue) - { - auto type = getWitnessTableType(existentialValue->getDataType()); - auto inst = createInst<IRInst>( - this, - kIROp_ExtractExistentialWitnessTable, - type, - 1, - &existentialValue); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitPrimalSubstituteInst(IRType* type, IRInst* baseFn) +{ + auto inst = createInst<IRPrimalSubstitute>(this, kIROp_PrimalSubstitute, type, baseFn); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitForwardDifferentiateInst(IRType* type, IRInst* baseFn) - { - auto inst = createInst<IRForwardDifferentiate>( - this, - kIROp_ForwardDifferentiate, - type, - baseFn); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitDetachDerivative(IRType* type, IRInst* value) +{ + auto inst = createInst<IRDetachDerivative>(this, kIROp_DetachDerivative, type, value); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitPrimalSubstituteInst(IRType* type, IRInst* baseFn) - { - auto inst = createInst<IRPrimalSubstitute>( - this, - kIROp_PrimalSubstitute, - type, - baseFn); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitIsDifferentialNull(IRInst* value) +{ + auto inst = + createInst<IRIsDifferentialNull>(this, kIROp_IsDifferentialNull, getBoolType(), value); + addInst(inst); + return inst; +} - IRInst *IRBuilder::emitDetachDerivative(IRType *type, IRInst *value) - { - auto inst = createInst<IRDetachDerivative>( - this, - kIROp_DetachDerivative, - type, - value); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitBackwardDifferentiateInst(IRType* type, IRInst* baseFn) +{ + auto inst = + createInst<IRBackwardDifferentiate>(this, kIROp_BackwardDifferentiate, type, baseFn); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitDispatchKernelInst( + IRType* type, + IRInst* baseFn, + IRInst* threadGroupSize, + IRInst* dispatchSize, + Int argCount, + IRInst* const* inArgs) +{ + List<IRInst*> args = {baseFn, threadGroupSize, dispatchSize}; + args.addRange(inArgs, (Index)argCount); + auto inst = createInst<IRDispatchKernel>( + this, + kIROp_DispatchKernel, + type, + (Int)args.getCount(), + args.getBuffer()); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitCudaKernelLaunch( + IRInst* baseFn, + IRInst* gridDim, + IRInst* blockDim, + IRInst* argsArray, + IRInst* cudaStream) +{ + IRInst* args[5] = {baseFn, gridDim, blockDim, argsArray, cudaStream}; + return emitIntrinsicInst(getVoidType(), kIROp_CudaKernelLaunch, 5, args); +} - IRInst *IRBuilder::emitIsDifferentialNull(IRInst *value) - { - auto inst = createInst<IRIsDifferentialNull>( - this, - kIROp_IsDifferentialNull, - getBoolType(), - value); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitGetTorchCudaStream() +{ + return emitIntrinsicInst(getPtrType(getVoidType()), kIROp_TorchGetCudaStream, 0, nullptr); +} - IRInst* IRBuilder::emitBackwardDifferentiateInst(IRType* type, IRInst* baseFn) +IRInst* IRBuilder::emitBackwardDifferentiatePrimalInst(IRType* type, IRInst* baseFn) +{ + auto inst = createInst<IRBackwardDifferentiatePrimal>( + this, + kIROp_BackwardDifferentiatePrimal, + type, + baseFn); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitBackwardDifferentiatePropagateInst(IRType* type, IRInst* baseFn) +{ + auto inst = createInst<IRBackwardDifferentiatePropagate>( + this, + kIROp_BackwardDifferentiatePropagate, + type, + baseFn); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitMakeDifferentialValuePair(IRType* type, IRInst* primal, IRInst* differential) +{ + SLANG_RELEASE_ASSERT(as<IRDifferentialPairType>(type)); + SLANG_RELEASE_ASSERT(as<IRDifferentialPairType>(type)->getValueType() != nullptr); + + IRInst* args[] = {primal, differential}; + auto inst = createInstWithTrailingArgs<IRMakeDifferentialPair>( + this, + kIROp_MakeDifferentialPair, + type, + 2, + args); + addInst(inst); + inst->sourceLoc = primal->sourceLoc; + return inst; +} + +IRInst* IRBuilder::emitMakeDifferentialPtrPair(IRType* type, IRInst* primal, IRInst* differential) +{ + SLANG_RELEASE_ASSERT(as<IRDifferentialPtrPairType>(type)); + SLANG_RELEASE_ASSERT(as<IRDifferentialPtrPairType>(type)->getValueType() != nullptr); + + IRInst* args[] = {primal, differential}; + auto inst = createInstWithTrailingArgs<IRMakeDifferentialPtrPair>( + this, + kIROp_MakeDifferentialPtrPair, + type, + 2, + args); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitMakeDifferentialPair(IRType* pairType, IRInst* primalVal, IRInst* diffVal) +{ + if (as<IRDifferentialPairType>(pairType)) { - auto inst = createInst<IRBackwardDifferentiate>( - this, - kIROp_BackwardDifferentiate, - type, - baseFn); - addInst(inst); - return inst; + return emitMakeDifferentialValuePair(pairType, primalVal, diffVal); } - - IRInst* IRBuilder::emitDispatchKernelInst(IRType* type, IRInst* baseFn, IRInst* threadGroupSize, IRInst* dispatchSize, Int argCount, IRInst* const* inArgs) + else if (as<IRDifferentialPtrPairType>(pairType)) { - List<IRInst*> args = {baseFn, threadGroupSize, dispatchSize}; - args.addRange(inArgs, (Index)argCount); - auto inst = createInst<IRDispatchKernel>( - this, - kIROp_DispatchKernel, - type, - (Int)args.getCount(), - args.getBuffer()); - addInst(inst); - return inst; + // Quick optimization: + // If primalVal and diffVal are extracted from the same pointer-pair, + // we can just use the pointer-pair directly. + // + if (auto primalPtrVal = as<IRDifferentialPtrPairGetPrimal>(primalVal)) + { + if (auto diffPtrVal = as<IRDifferentialPtrPairGetDifferential>(diffVal)) + { + if (primalPtrVal->getBase() == diffPtrVal->getBase()) + return primalPtrVal->getBase(); + } + } + return emitMakeDifferentialPtrPair(pairType, primalVal, diffVal); } - - IRInst* IRBuilder::emitCudaKernelLaunch(IRInst* baseFn, IRInst* gridDim, IRInst* blockDim, IRInst* argsArray, IRInst* cudaStream) + else { - IRInst* args[5] = {baseFn, gridDim, blockDim, argsArray, cudaStream}; - return emitIntrinsicInst( - getVoidType(), - kIROp_CudaKernelLaunch, - 5, - args); + SLANG_ASSERT(!"unreachable"); + return nullptr; } +} - IRInst* IRBuilder::emitGetTorchCudaStream() +IRInst* IRBuilder::emitDifferentialPairGetDifferential(IRType* diffType, IRInst* pairVal) +{ + if (as<IRDifferentialPairType>(pairVal->getDataType())) { - return emitIntrinsicInst(getPtrType(getVoidType()), kIROp_TorchGetCudaStream, 0, nullptr); + return emitDifferentialValuePairGetDifferential(diffType, pairVal); } - - IRInst* IRBuilder::emitBackwardDifferentiatePrimalInst(IRType* type, IRInst* baseFn) + else if (as<IRDifferentialPtrPairType>(pairVal->getDataType())) { - auto inst = createInst<IRBackwardDifferentiatePrimal>( - this, - kIROp_BackwardDifferentiatePrimal, - type, - baseFn); - addInst(inst); - return inst; + return emitDifferentialPtrPairGetDifferential(diffType, pairVal); } - - IRInst* IRBuilder::emitBackwardDifferentiatePropagateInst(IRType* type, IRInst* baseFn) + else { - auto inst = createInst<IRBackwardDifferentiatePropagate>( - this, - kIROp_BackwardDifferentiatePropagate, - type, - baseFn); - addInst(inst); - return inst; + SLANG_ASSERT(!"unreachable"); + return nullptr; } +} - IRInst* IRBuilder::emitMakeDifferentialValuePair(IRType* type, IRInst* primal, IRInst* differential) +IRInst* IRBuilder::emitDifferentialPairGetPrimal(IRInst* pairVal) +{ + if (as<IRDifferentialPairType>(pairVal->getDataType())) { - SLANG_RELEASE_ASSERT(as<IRDifferentialPairType>(type)); - SLANG_RELEASE_ASSERT(as<IRDifferentialPairType>(type)->getValueType() != nullptr); - - IRInst* args[] = {primal, differential}; - auto inst = createInstWithTrailingArgs<IRMakeDifferentialPair>( - this, kIROp_MakeDifferentialPair, type, 2, args); - addInst(inst); - inst->sourceLoc = primal->sourceLoc; - return inst; + return emitDifferentialValuePairGetPrimal(pairVal); } - - IRInst* IRBuilder::emitMakeDifferentialPtrPair(IRType* type, IRInst* primal, IRInst* differential) + else if (as<IRDifferentialPtrPairType>(pairVal->getDataType())) { - SLANG_RELEASE_ASSERT(as<IRDifferentialPtrPairType>(type)); - SLANG_RELEASE_ASSERT(as<IRDifferentialPtrPairType>(type)->getValueType() != nullptr); - - IRInst* args[] = {primal, differential}; - auto inst = createInstWithTrailingArgs<IRMakeDifferentialPtrPair>( - this, kIROp_MakeDifferentialPtrPair, type, 2, args); - addInst(inst); - return inst; + return emitDifferentialPtrPairGetPrimal(pairVal); } - - IRInst* IRBuilder::emitMakeDifferentialPair(IRType* pairType, IRInst* primalVal, IRInst* diffVal) + else { - if (as<IRDifferentialPairType>(pairType)) - { - return emitMakeDifferentialValuePair(pairType, primalVal, diffVal); - } - else if (as<IRDifferentialPtrPairType>(pairType)) - { - // Quick optimization: - // If primalVal and diffVal are extracted from the same pointer-pair, - // we can just use the pointer-pair directly. - // - if (auto primalPtrVal = as<IRDifferentialPtrPairGetPrimal>(primalVal)) - { - if (auto diffPtrVal = as<IRDifferentialPtrPairGetDifferential>(diffVal)) - { - if (primalPtrVal->getBase() == diffPtrVal->getBase()) - return primalPtrVal->getBase(); - } - } - return emitMakeDifferentialPtrPair(pairType, primalVal, diffVal); - } - else - { - SLANG_ASSERT(!"unreachable"); - return nullptr; - } + SLANG_ASSERT(!"unreachable"); + return nullptr; } +} - IRInst* IRBuilder::emitDifferentialPairGetDifferential(IRType* diffType, IRInst* pairVal) +IRInst* IRBuilder::emitDifferentialPairGetPrimal(IRType* primalType, IRInst* pairVal) +{ + if (as<IRDifferentialPairType>(pairVal->getDataType())) { - if (as<IRDifferentialPairType>(pairVal->getDataType())) - { - return emitDifferentialValuePairGetDifferential(diffType, pairVal); - } - else if (as<IRDifferentialPtrPairType>(pairVal->getDataType())) - { - return emitDifferentialPtrPairGetDifferential(diffType, pairVal); - } - else - { - SLANG_ASSERT(!"unreachable"); - return nullptr; - } + return emitDifferentialValuePairGetPrimal(primalType, pairVal); } - - IRInst* IRBuilder::emitDifferentialPairGetPrimal(IRInst* pairVal) + else if (as<IRDifferentialPtrPairType>(pairVal->getDataType())) { - if (as<IRDifferentialPairType>(pairVal->getDataType())) - { - return emitDifferentialValuePairGetPrimal(pairVal); - } - else if (as<IRDifferentialPtrPairType>(pairVal->getDataType())) - { - return emitDifferentialPtrPairGetPrimal(pairVal); - } - else - { - SLANG_ASSERT(!"unreachable"); - return nullptr; - } + return emitDifferentialPtrPairGetPrimal(primalType, pairVal); } - - IRInst* IRBuilder::emitDifferentialPairGetPrimal(IRType* primalType, IRInst* pairVal) + else { - if (as<IRDifferentialPairType>(pairVal->getDataType())) - { - return emitDifferentialValuePairGetPrimal(primalType, pairVal); - } - else if (as<IRDifferentialPtrPairType>(pairVal->getDataType())) - { - return emitDifferentialPtrPairGetPrimal(primalType, pairVal); - } - else - { - SLANG_ASSERT(!"unreachable"); - return nullptr; - } + SLANG_ASSERT(!"unreachable"); + return nullptr; } +} - IRInst* IRBuilder::emitMakeDifferentialPairUserCode(IRType* type, IRInst* primal, IRInst* differential) +IRInst* IRBuilder::emitMakeDifferentialPairUserCode( + IRType* type, + IRInst* primal, + IRInst* differential) +{ + SLANG_RELEASE_ASSERT(as<IRDifferentialPairTypeBase>(type)); + SLANG_RELEASE_ASSERT(as<IRDifferentialPairTypeBase>(type)->getValueType() != nullptr); + + IRInst* args[] = {primal, differential}; + auto inst = createInstWithTrailingArgs<IRMakeDifferentialPair>( + this, + kIROp_MakeDifferentialPairUserCode, + type, + 2, + args); + addInst(inst); + inst->sourceLoc = primal->sourceLoc; + return inst; +} + +IRInst* IRBuilder::emitSpecializeInst( + IRType* type, + IRInst* genericVal, + UInt argCount, + IRInst* const* args) +{ + auto innerReturnVal = findInnerMostGenericReturnVal(as<IRGeneric>(genericVal)); + if (as<IRWitnessTable>(innerReturnVal)) { - SLANG_RELEASE_ASSERT(as<IRDifferentialPairTypeBase>(type)); - SLANG_RELEASE_ASSERT(as<IRDifferentialPairTypeBase>(type)->getValueType() != nullptr); - - IRInst* args[] = { primal, differential }; - auto inst = createInstWithTrailingArgs<IRMakeDifferentialPair>( - this, kIROp_MakeDifferentialPairUserCode, type, 2, args); - addInst(inst); - inst->sourceLoc = primal->sourceLoc; - return inst; + return createIntrinsicInst(type, kIROp_Specialize, genericVal, argCount, args); } - IRInst* IRBuilder::emitSpecializeInst( - IRType* type, - IRInst* genericVal, - UInt argCount, - IRInst* const* args) - { - auto innerReturnVal = findInnerMostGenericReturnVal(as<IRGeneric>(genericVal)); - if (as<IRWitnessTable>(innerReturnVal)) - { - return createIntrinsicInst( - type, - kIROp_Specialize, - genericVal, - argCount, - args); - } + auto inst = createInstWithTrailingArgs<IRSpecialize>( + this, + kIROp_Specialize, + type, + 1, + &genericVal, + argCount, + args); - auto inst = createInstWithTrailingArgs<IRSpecialize>( - this, - kIROp_Specialize, - type, - 1, - &genericVal, - argCount, - args); - - if (!inst->parent) - addInst(inst); - return inst; - } - - IRInst* IRBuilder::emitExpandInst(IRType* type, UInt capturedArgCount, IRInst* const* capturedArgs) - { - auto inst = createInstWithTrailingArgs<IRSpecialize>( - this, - kIROp_Expand, - type, - capturedArgCount, - capturedArgs, - 0, - nullptr); + if (!inst->parent) addInst(inst); - return inst; - } + return inst; +} - IRInst* IRBuilder::emitEachInst(IRType* type, IRInst* base, IRInst* indexArg) - { - IRInst* args[] = { base, indexArg }; - return emitIntrinsicInst(type, kIROp_Each, indexArg ? 2 : 1, args); - } - - IRInst* IRBuilder::emitLookupInterfaceMethodInst( - IRType* type, - IRInst* witnessTableVal, - IRInst* interfaceMethodVal) - { - // TODO: if somebody tries to declare a struct that inherits - // an interface conformance from a base type, then we hit - // this assert. The problem should be fixed higher up in - // the emit logic, but this is a reasonably early place - // to catch it. - // - SLANG_ASSERT(witnessTableVal && witnessTableVal->getOp() != kIROp_StructKey); - - IRInst* args[] = {witnessTableVal, interfaceMethodVal}; +IRInst* IRBuilder::emitExpandInst(IRType* type, UInt capturedArgCount, IRInst* const* capturedArgs) +{ + auto inst = createInstWithTrailingArgs<IRSpecialize>( + this, + kIROp_Expand, + type, + capturedArgCount, + capturedArgs, + 0, + nullptr); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitEachInst(IRType* type, IRInst* base, IRInst* indexArg) +{ + IRInst* args[] = {base, indexArg}; + return emitIntrinsicInst(type, kIROp_Each, indexArg ? 2 : 1, args); +} + +IRInst* IRBuilder::emitLookupInterfaceMethodInst( + IRType* type, + IRInst* witnessTableVal, + IRInst* interfaceMethodVal) +{ + // TODO: if somebody tries to declare a struct that inherits + // an interface conformance from a base type, then we hit + // this assert. The problem should be fixed higher up in + // the emit logic, but this is a reasonably early place + // to catch it. + // + SLANG_ASSERT(witnessTableVal && witnessTableVal->getOp() != kIROp_StructKey); - return createIntrinsicInst( - type, - kIROp_LookupWitness, - 2, - args); - } + IRInst* args[] = {witnessTableVal, interfaceMethodVal}; - IRInst* IRBuilder::emitGetSequentialIDInst(IRInst* rttiObj) - { - auto inst = createInst<IRAlloca>(this, kIROp_GetSequentialID, getUIntType(), rttiObj); + return createIntrinsicInst(type, kIROp_LookupWitness, 2, args); +} - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitGetSequentialIDInst(IRInst* rttiObj) +{ + auto inst = createInst<IRAlloca>(this, kIROp_GetSequentialID, getUIntType(), rttiObj); - IRInst* IRBuilder::emitAlloca(IRInst* type, IRInst* rttiObjPtr) - { - auto inst = createInst<IRAlloca>( - this, - kIROp_Alloca, - (IRType*)type, - rttiObjPtr); + addInst(inst); + return inst; +} - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitAlloca(IRInst* type, IRInst* rttiObjPtr) +{ + auto inst = createInst<IRAlloca>(this, kIROp_Alloca, (IRType*)type, rttiObjPtr); - IRInst* IRBuilder::emitGlobalValueRef(IRInst* globalInst) - { - auto inst = createInst<IRGlobalValueRef>( - this, - kIROp_GlobalValueRef, - (IRType*)globalInst->getFullType(), - globalInst); + addInst(inst); + return inst; +} - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitGlobalValueRef(IRInst* globalInst) +{ + auto inst = createInst<IRGlobalValueRef>( + this, + kIROp_GlobalValueRef, + (IRType*)globalInst->getFullType(), + globalInst); - IRInst* IRBuilder::emitPackAnyValue(IRType* type, IRInst* value) - { - auto inst = createInst<IRPackAnyValue>( - this, - kIROp_PackAnyValue, - type, - value); + addInst(inst); + return inst; +} - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitPackAnyValue(IRType* type, IRInst* value) +{ + auto inst = createInst<IRPackAnyValue>(this, kIROp_PackAnyValue, type, value); - IRInst* IRBuilder::emitUnpackAnyValue(IRType* type, IRInst* value) - { - auto inst = createInst<IRPackAnyValue>( - this, - kIROp_UnpackAnyValue, - type, - value); + addInst(inst); + return inst; +} - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitUnpackAnyValue(IRType* type, IRInst* value) +{ + auto inst = createInst<IRPackAnyValue>(this, kIROp_UnpackAnyValue, type, value); - IRCall* IRBuilder::emitCallInst( - IRType* type, - IRInst* pFunc, - UInt argCount, - IRInst* const* args) - { - auto inst = createInstWithTrailingArgs<IRCall>( - this, - kIROp_Call, - type, - 1, - &pFunc, - argCount, - args); - addInst(inst); - return inst; - } + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitTryCallInst( - IRType* type, - IRBlock* successBlock, - IRBlock* failureBlock, - IRInst* func, - UInt argCount, - IRInst* const* args) - { - IRInst* fixedArgs[] = {successBlock, failureBlock, func}; - auto inst = createInstWithTrailingArgs<IRTryCall>( - this, kIROp_TryCall, type, 3, fixedArgs, argCount, args); +IRCall* IRBuilder::emitCallInst(IRType* type, IRInst* pFunc, UInt argCount, IRInst* const* args) +{ + auto inst = + createInstWithTrailingArgs<IRCall>(this, kIROp_Call, type, 1, &pFunc, argCount, args); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitTryCallInst( + IRType* type, + IRBlock* successBlock, + IRBlock* failureBlock, + IRInst* func, + UInt argCount, + IRInst* const* args) +{ + IRInst* fixedArgs[] = {successBlock, failureBlock, func}; + auto inst = createInstWithTrailingArgs<IRTryCall>( + this, + kIROp_TryCall, + type, + 3, + fixedArgs, + argCount, + args); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::createIntrinsicInst(IRType* type, IROp op, UInt argCount, IRInst* const* args) +{ + return createInstWithTrailingArgs<IRInst>(this, op, type, argCount, args); +} + +IRInst* IRBuilder::createIntrinsicInst( + IRType* type, + IROp op, + IRInst* operand, + UInt operandCount, + IRInst* const* operands) +{ + return createInstWithTrailingArgs<IRInst>(this, op, type, operand, operandCount, operands); +} + +IRInst* IRBuilder::createIntrinsicInst( + IRType* type, + IROp op, + UInt operandListCount, + UInt const* listOperandCounts, + IRInst* const* const* listOperands) +{ + return createInstImpl<IRInst>( + this, + op, + type, + 0, + nullptr, + (Int)operandListCount, + (Int const*)listOperandCounts, + listOperands); +} + + +IRInst* IRBuilder::emitIntrinsicInst(IRType* type, IROp op, UInt argCount, IRInst* const* args) +{ + auto inst = createIntrinsicInst(type, op, argCount, args); + if (!inst->parent) addInst(inst); - return inst; - } + return inst; +} - IRInst* IRBuilder::createIntrinsicInst( - IRType* type, - IROp op, - UInt argCount, - IRInst* const* args) - { - return createInstWithTrailingArgs<IRInst>( - this, - op, - type, - argCount, - args); - } - - IRInst* IRBuilder::createIntrinsicInst( - IRType* type, IROp op, IRInst* operand, UInt operandCount, IRInst* const* operands) - { - return createInstWithTrailingArgs<IRInst>(this, op, type, operand, operandCount, operands); - } - - IRInst* IRBuilder::createIntrinsicInst(IRType* type, IROp op, UInt operandListCount, UInt const* listOperandCounts, IRInst* const* const* listOperands) - { - return createInstImpl<IRInst>(this, op, type, 0, nullptr, (Int)operandListCount, (Int const* )listOperandCounts, listOperands); - } - - - IRInst* IRBuilder::emitIntrinsicInst( - IRType* type, - IROp op, - UInt argCount, - IRInst* const* args) - { - auto inst = createIntrinsicInst( - type, - op, - argCount, - args); - if (!inst->parent) - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitDefaultConstructRaw(IRType* type) +{ + return emitIntrinsicInst(type, kIROp_DefaultConstruct, 0, nullptr); +} - IRInst* IRBuilder::emitDefaultConstructRaw(IRType* type) +IRInst* IRBuilder::emitDefaultConstruct(IRType* type, bool fallback) +{ + IRType* actualType = type; + for (;;) { - return emitIntrinsicInst(type, kIROp_DefaultConstruct, 0, nullptr); + if (auto attr = as<IRAttributedType>(actualType)) + actualType = attr->getBaseType(); + else if (auto rateQualified = as<IRRateQualifiedType>(actualType)) + actualType = rateQualified->getValueType(); + else + break; } - - IRInst* IRBuilder::emitDefaultConstruct(IRType* type, bool fallback) - { - IRType* actualType = type; - for (;;) - { - if (auto attr = as<IRAttributedType>(actualType)) - actualType = attr->getBaseType(); - else if (auto rateQualified = as<IRRateQualifiedType>(actualType)) - actualType = rateQualified->getValueType(); - else - break; - } - switch (actualType->getOp()) - { - case kIROp_Int8Type: - case kIROp_Int16Type: - case kIROp_IntType: - case kIROp_IntPtrType: - case kIROp_Int64Type: - case kIROp_UInt8Type: - case kIROp_UInt16Type: - case kIROp_UIntType: - case kIROp_UIntPtrType: - case kIROp_UInt64Type: - case kIROp_CharType: - return getIntValue(type, 0); - case kIROp_BoolType: - return getBoolValue(false); - case kIROp_FloatType: - case kIROp_HalfType: - case kIROp_DoubleType: - return getFloatValue(type, 0.0); - case kIROp_VoidType: - return getVoidValue(); - case kIROp_StringType: - return getStringValue(UnownedStringSlice()); - case kIROp_PtrType: - case kIROp_InOutType: - case kIROp_OutType: - case kIROp_RawPointerType: - case kIROp_RefType: - case kIROp_ConstRefType: - case kIROp_ComPtrType: - case kIROp_NativePtrType: - case kIROp_NativeStringType: - return getNullPtrValue(type); - case kIROp_OptionalType: - { - auto inner = emitDefaultConstruct(as<IROptionalType>(actualType)->getValueType(), fallback); + switch (actualType->getOp()) + { + case kIROp_Int8Type: + case kIROp_Int16Type: + case kIROp_IntType: + case kIROp_IntPtrType: + case kIROp_Int64Type: + case kIROp_UInt8Type: + case kIROp_UInt16Type: + case kIROp_UIntType: + case kIROp_UIntPtrType: + case kIROp_UInt64Type: + case kIROp_CharType: return getIntValue(type, 0); + case kIROp_BoolType: return getBoolValue(false); + case kIROp_FloatType: + case kIROp_HalfType: + case kIROp_DoubleType: return getFloatValue(type, 0.0); + case kIROp_VoidType: return getVoidValue(); + case kIROp_StringType: return getStringValue(UnownedStringSlice()); + case kIROp_PtrType: + case kIROp_InOutType: + case kIROp_OutType: + case kIROp_RawPointerType: + case kIROp_RefType: + case kIROp_ConstRefType: + case kIROp_ComPtrType: + case kIROp_NativePtrType: + case kIROp_NativeStringType: return getNullPtrValue(type); + case kIROp_OptionalType: + { + auto inner = + emitDefaultConstruct(as<IROptionalType>(actualType)->getValueType(), fallback); if (!inner) return nullptr; return emitMakeOptionalNone(type, inner); } - case kIROp_TupleType: + case kIROp_TupleType: { List<IRInst*> elements; auto tupleType = as<IRTupleType>(actualType); @@ -3916,7 +3754,7 @@ namespace Slang } return emitMakeTuple(type, elements); } - case kIROp_StructType: + case kIROp_StructType: { List<IRInst*> elements; auto structType = as<IRStructType>(actualType); @@ -3930,7 +3768,7 @@ namespace Slang } return emitMakeStruct(type, elements); } - case kIROp_ArrayType: + case kIROp_ArrayType: { auto arrayType = as<IRArrayType>(actualType); if (auto count = as<IRIntLit>(arrayType->getElementCount())) @@ -3950,4840 +3788,4377 @@ namespace Slang } break; } - case kIROp_VectorType: + case kIROp_VectorType: { - auto inner = emitDefaultConstruct(as<IRVectorType>(actualType)->getElementType(), fallback); + auto inner = + emitDefaultConstruct(as<IRVectorType>(actualType)->getElementType(), fallback); if (!inner) return nullptr; return emitIntrinsicInst(type, kIROp_MakeVectorFromScalar, 1, &inner); } - case kIROp_MatrixType: + case kIROp_MatrixType: { - auto inner = emitDefaultConstruct(as<IRMatrixType>(actualType)->getElementType(), fallback); + auto inner = + emitDefaultConstruct(as<IRMatrixType>(actualType)->getElementType(), fallback); if (!inner) return nullptr; return emitIntrinsicInst(type, kIROp_MakeMatrixFromScalar, 1, &inner); } - default: - break; - } - if (fallback) - { - return emitIntrinsicInst(type, kIROp_DefaultConstruct, 0, nullptr); - } - return nullptr; + default: break; } - - IRInst* IRBuilder::emitEmbeddedDownstreamIR(CodeGenTarget target, ISlangBlob *blob) + if (fallback) { - IRInst* args[] = { getIntValue(getIntType(), (int)target), getBlobValue(blob) }; - - return emitIntrinsicInst(getVoidType(), kIROp_EmbeddedDownstreamIR, 2, args); + return emitIntrinsicInst(type, kIROp_DefaultConstruct, 0, nullptr); } + return nullptr; +} + +IRInst* IRBuilder::emitEmbeddedDownstreamIR(CodeGenTarget target, ISlangBlob* blob) +{ + IRInst* args[] = {getIntValue(getIntType(), (int)target), getBlobValue(blob)}; + + return emitIntrinsicInst(getVoidType(), kIROp_EmbeddedDownstreamIR, 2, args); +} - enum class TypeCastStyle +enum class TypeCastStyle +{ + Unknown = -1, + Int, + Float, + Bool, + Ptr, + Void +}; +static TypeCastStyle _getTypeStyleId(IRType* type) +{ + if (auto vectorType = as<IRVectorType>(type)) { - Unknown = -1, - Int, Float, Bool, Ptr, Void - }; - static TypeCastStyle _getTypeStyleId(IRType* type) + return _getTypeStyleId(vectorType->getElementType()); + } + if (auto matrixType = as<IRMatrixType>(type)) { - if (auto vectorType = as<IRVectorType>(type)) - { - return _getTypeStyleId(vectorType->getElementType()); - } - if(auto matrixType = as<IRMatrixType>(type)) - { - return _getTypeStyleId(matrixType->getElementType()); - } - auto style = getTypeStyle(type->getOp()); - switch (style) - { - case kIROp_IntType: - return TypeCastStyle::Int; - case kIROp_FloatType: - case kIROp_HalfType: - case kIROp_DoubleType: - return TypeCastStyle::Float; - case kIROp_BoolType: - return TypeCastStyle::Bool; - case kIROp_PtrType: - case kIROp_InOutType: - case kIROp_OutType: - case kIROp_RawPointerType: - case kIROp_RefType: - case kIROp_ConstRefType: - return TypeCastStyle::Ptr; - case kIROp_VoidType: - return TypeCastStyle::Void; - default: - return TypeCastStyle::Unknown; - } + return _getTypeStyleId(matrixType->getElementType()); } - - IRInst* IRBuilder::emitCast(IRType* type, IRInst* value) + auto style = getTypeStyle(type->getOp()); + switch (style) { - if (isTypeEqual(type, value->getDataType())) - return value; - - auto toStyle = _getTypeStyleId(type); - auto fromStyle = _getTypeStyleId(value->getDataType()); + case kIROp_IntType: return TypeCastStyle::Int; + case kIROp_FloatType: + case kIROp_HalfType: + case kIROp_DoubleType: return TypeCastStyle::Float; + case kIROp_BoolType: return TypeCastStyle::Bool; + case kIROp_PtrType: + case kIROp_InOutType: + case kIROp_OutType: + case kIROp_RawPointerType: + case kIROp_RefType: + case kIROp_ConstRefType: return TypeCastStyle::Ptr; + case kIROp_VoidType: return TypeCastStyle::Void; + default: return TypeCastStyle::Unknown; + } +} - if (fromStyle == TypeCastStyle::Void) - { - // We shouldn't be casting from void to other types. - SLANG_UNREACHABLE("cast from void type"); - } +IRInst* IRBuilder::emitCast(IRType* type, IRInst* value) +{ + if (isTypeEqual(type, value->getDataType())) + return value; - SLANG_RELEASE_ASSERT(toStyle != TypeCastStyle::Unknown); - SLANG_RELEASE_ASSERT(fromStyle != TypeCastStyle::Unknown); + auto toStyle = _getTypeStyleId(type); + auto fromStyle = _getTypeStyleId(value->getDataType()); - struct OpSeq - { - IROp op0, op1; - OpSeq(IROp op) - { - op0 = op; op1 = kIROp_Nop; - } - OpSeq(IROp op, IROp inOp1) - { - op0 = op; op1 = inOp1; - } - }; + if (fromStyle == TypeCastStyle::Void) + { + // We shouldn't be casting from void to other types. + SLANG_UNREACHABLE("cast from void type"); + } - static const OpSeq opMap[4][5] = - { - /* To: Int, Float, Bool, Ptr, Void*/ - /* From Int */ {kIROp_IntCast, kIROp_CastIntToFloat, kIROp_IntCast, kIROp_CastIntToPtr, kIROp_CastToVoid }, - /* From Float */ {kIROp_CastFloatToInt, kIROp_FloatCast, {kIROp_CastFloatToInt, kIROp_IntCast}, {kIROp_CastFloatToInt, kIROp_CastIntToPtr}, kIROp_CastToVoid}, - /* From Bool */ {kIROp_IntCast, kIROp_CastIntToFloat, kIROp_Nop, kIROp_CastIntToPtr, kIROp_CastToVoid}, - /* From Ptr */ {kIROp_CastPtrToInt, {kIROp_CastPtrToInt, kIROp_CastIntToFloat}, kIROp_CastPtrToBool, kIROp_BitCast, kIROp_CastToVoid}, - }; - - auto op = opMap[(int)fromStyle][(int)toStyle]; - if (op.op0 == kIROp_Nop) - return value; - auto t = type; - if (op.op1 != kIROp_Nop) + SLANG_RELEASE_ASSERT(toStyle != TypeCastStyle::Unknown); + SLANG_RELEASE_ASSERT(fromStyle != TypeCastStyle::Unknown); + + struct OpSeq + { + IROp op0, op1; + OpSeq(IROp op) { - t = getUInt64Type(); + op0 = op; + op1 = kIROp_Nop; } - auto result = emitIntrinsicInst(t, op.op0, 1, &value); - if (op.op1 != kIROp_Nop) + OpSeq(IROp op, IROp inOp1) { - result = emitIntrinsicInst(type, op.op1, 1, &result); + op0 = op; + op1 = inOp1; } - return result; + }; + + static const OpSeq opMap[4][5] = { + /* To: Int, Float, Bool, Ptr, Void*/ + /* From Int */ { + kIROp_IntCast, + kIROp_CastIntToFloat, + kIROp_IntCast, + kIROp_CastIntToPtr, + kIROp_CastToVoid}, + /* From Float */ + {kIROp_CastFloatToInt, + kIROp_FloatCast, + {kIROp_CastFloatToInt, kIROp_IntCast}, + {kIROp_CastFloatToInt, kIROp_CastIntToPtr}, + kIROp_CastToVoid}, + /* From Bool */ + {kIROp_IntCast, kIROp_CastIntToFloat, kIROp_Nop, kIROp_CastIntToPtr, kIROp_CastToVoid}, + /* From Ptr */ + {kIROp_CastPtrToInt, + {kIROp_CastPtrToInt, kIROp_CastIntToFloat}, + kIROp_CastPtrToBool, + kIROp_BitCast, + kIROp_CastToVoid}, + }; + + auto op = opMap[(int)fromStyle][(int)toStyle]; + if (op.op0 == kIROp_Nop) + return value; + auto t = type; + if (op.op1 != kIROp_Nop) + { + t = getUInt64Type(); + } + auto result = emitIntrinsicInst(t, op.op0, 1, &value); + if (op.op1 != kIROp_Nop) + { + result = emitIntrinsicInst(type, op.op1, 1, &result); } + return result; +} - IRInst* IRBuilder::emitVectorReshape(IRType* type, IRInst* value) +IRInst* IRBuilder::emitVectorReshape(IRType* type, IRInst* value) +{ + auto targetVectorType = as<IRVectorType>(type); + auto sourceVectorType = as<IRVectorType>(value->getDataType()); + if (targetVectorType && !sourceVectorType) { - auto targetVectorType = as<IRVectorType>(type); - auto sourceVectorType = as<IRVectorType>(value->getDataType()); - if (targetVectorType && !sourceVectorType) + auto elementType = targetVectorType->getElementType(); + Index elemCount = 1; + if (auto intLit = as<IRIntLit>(targetVectorType->getElementCount())) { - auto elementType = targetVectorType->getElementType(); - Index elemCount = 1; - if(auto intLit = as<IRIntLit>(targetVectorType->getElementCount())) - { - elemCount = (Index)intLit->getValue(); - } - IRInst* zeroVal = emitDefaultConstruct(elementType); - List<IRInst*> defaultVals; - defaultVals.reserve(elemCount); - defaultVals.add(value); - for(auto i = 1; i < elemCount; i++) - defaultVals.add(zeroVal); - return emitMakeVector(targetVectorType, defaultVals); + elemCount = (Index)intLit->getValue(); } - else if (!targetVectorType) + IRInst* zeroVal = emitDefaultConstruct(elementType); + List<IRInst*> defaultVals; + defaultVals.reserve(elemCount); + defaultVals.add(value); + for (auto i = 1; i < elemCount; i++) + defaultVals.add(zeroVal); + return emitMakeVector(targetVectorType, defaultVals); + } + else if (!targetVectorType) + { + if (!sourceVectorType) + return emitCast(targetVectorType, value); + else { - if (!sourceVectorType) - return emitCast(targetVectorType, value); - else - { - UInt index = 0; - return emitCast(type, emitSwizzle(sourceVectorType->getElementType(), value, 1, &index)); - } + UInt index = 0; + return emitCast( + type, + emitSwizzle(sourceVectorType->getElementType(), value, 1, &index)); } - if (targetVectorType->getElementCount() != sourceVectorType->getElementCount()) + } + if (targetVectorType->getElementCount() != sourceVectorType->getElementCount()) + { + auto fromCount = as<IRIntLit>(sourceVectorType->getElementCount()); + auto toCount = as<IRIntLit>(targetVectorType->getElementCount()); + if (fromCount && toCount) { - auto fromCount = as<IRIntLit>(sourceVectorType->getElementCount()); - auto toCount = as<IRIntLit>(targetVectorType->getElementCount()); - if (fromCount && toCount) + if (toCount->getValue() < fromCount->getValue()) + { + List<UInt> indices; + for (UInt i = 0; i < (UInt)toCount->getValue(); i++) + indices.add(i); + return emitSwizzle( + targetVectorType, + value, + (UInt)indices.getCount(), + indices.getBuffer()); + } + else if (toCount->getValue() > fromCount->getValue()) { - if (toCount->getValue() < fromCount->getValue()) + List<IRInst*> args; + for (UInt i = 0; i < (UInt)fromCount->getValue(); i++) { - List<UInt> indices; - for (UInt i = 0; i < (UInt)toCount->getValue(); i++) - indices.add(i); - return emitSwizzle(targetVectorType, value, (UInt)indices.getCount(), indices.getBuffer()); + auto element = emitSwizzle(sourceVectorType->getElementType(), value, 1, &i); + args.add(element); } - else if (toCount->getValue() > fromCount->getValue()) + for (IRIntegerValue i = fromCount->getValue(); i < toCount->getValue(); i++) { - List<IRInst*> args; - for (UInt i = 0; i < (UInt)fromCount->getValue(); i++) - { - auto element = emitSwizzle(sourceVectorType->getElementType(), value , 1, &i); - args.add(element); - } - for (IRIntegerValue i = fromCount->getValue(); i < toCount->getValue(); i++) - { - args.add(emitDefaultConstruct(targetVectorType->getElementType())); - } - return emitMakeVector(targetVectorType, args); + args.add(emitDefaultConstruct(targetVectorType->getElementType())); } + return emitMakeVector(targetVectorType, args); } - auto reshape = emitIntrinsicInst( - getVectorType( - sourceVectorType->getElementType(), targetVectorType->getElementCount()), - kIROp_VectorReshape, - 1, - &value); - return emitCast(type, reshape); } - return value; + auto reshape = emitIntrinsicInst( + getVectorType(sourceVectorType->getElementType(), targetVectorType->getElementCount()), + kIROp_VectorReshape, + 1, + &value); + return emitCast(type, reshape); } + return value; +} - IRInst* IRBuilder::emitMakeUInt64(IRInst* low, IRInst* high) - { - IRInst* args[2] = {low, high}; - return emitIntrinsicInst(getUInt64Type(), kIROp_MakeUInt64, 2, args); - } +IRInst* IRBuilder::emitMakeUInt64(IRInst* low, IRInst* high) +{ + IRInst* args[2] = {low, high}; + return emitIntrinsicInst(getUInt64Type(), kIROp_MakeUInt64, 2, args); +} - IRInst* IRBuilder::emitMakeRTTIObject(IRInst* typeInst) - { - auto inst = createInst<IRRTTIObject>( - this, - kIROp_RTTIObject, - getRTTIType(), - typeInst); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitMakeRTTIObject(IRInst* typeInst) +{ + auto inst = createInst<IRRTTIObject>(this, kIROp_RTTIObject, getRTTIType(), typeInst); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitMakeValuePack(IRType* type, UInt count, IRInst* const* args) - { - return emitIntrinsicInst(type, kIROp_MakeValuePack, count, args); - } +IRInst* IRBuilder::emitMakeValuePack(IRType* type, UInt count, IRInst* const* args) +{ + return emitIntrinsicInst(type, kIROp_MakeValuePack, count, args); +} - IRInst* IRBuilder::emitMakeValuePack(UInt count, IRInst* const* args) - { - ShortList<IRType*> types; - for (UInt i = 0; i < count; ++i) - types.add(args[i]->getFullType()); +IRInst* IRBuilder::emitMakeValuePack(UInt count, IRInst* const* args) +{ + ShortList<IRType*> types; + for (UInt i = 0; i < count; ++i) + types.add(args[i]->getFullType()); - auto type = getTypePack((UInt)types.getCount(), types.getArrayView().getBuffer()); - return emitIntrinsicInst(type, kIROp_MakeValuePack, count, args); - } + auto type = getTypePack((UInt)types.getCount(), types.getArrayView().getBuffer()); + return emitIntrinsicInst(type, kIROp_MakeValuePack, count, args); +} - IRInst* IRBuilder::emitMakeTuple(IRType* type, UInt count, IRInst* const* args) - { - return emitIntrinsicInst(type, kIROp_MakeTuple, count, args); - } +IRInst* IRBuilder::emitMakeTuple(IRType* type, UInt count, IRInst* const* args) +{ + return emitIntrinsicInst(type, kIROp_MakeTuple, count, args); +} - IRInst* IRBuilder::emitMakeTargetTuple(IRType* type, UInt count, IRInst* const* args) - { - return emitIntrinsicInst(type, kIROp_MakeTargetTuple, count, args); - } +IRInst* IRBuilder::emitMakeTargetTuple(IRType* type, UInt count, IRInst* const* args) +{ + return emitIntrinsicInst(type, kIROp_MakeTargetTuple, count, args); +} - IRInst* IRBuilder::emitTargetTupleGetElement(IRType* elementType, IRInst* targetTupleVal, IRInst* indexVal) - { - IRInst* args[] = {targetTupleVal, indexVal}; - return emitIntrinsicInst(elementType, kIROp_GetTargetTupleElement, 2, args); - } +IRInst* IRBuilder::emitTargetTupleGetElement( + IRType* elementType, + IRInst* targetTupleVal, + IRInst* indexVal) +{ + IRInst* args[] = {targetTupleVal, indexVal}; + return emitIntrinsicInst(elementType, kIROp_GetTargetTupleElement, 2, args); +} - IRInst* IRBuilder::emitMakeTuple(UInt count, IRInst* const* args) - { - List<IRType*> types; - for(UInt i = 0; i < count; ++i) - types.add(args[i]->getFullType()); +IRInst* IRBuilder::emitMakeTuple(UInt count, IRInst* const* args) +{ + List<IRType*> types; + for (UInt i = 0; i < count; ++i) + types.add(args[i]->getFullType()); - auto type = getTupleType(types); - return emitMakeTuple(type, count, args); - } + auto type = getTupleType(types); + return emitMakeTuple(type, count, args); +} - IRInst* IRBuilder::emitMakeString(IRInst* nativeStr) - { - return emitIntrinsicInst(getStringType(), kIROp_MakeString, 1, &nativeStr); - } +IRInst* IRBuilder::emitMakeString(IRInst* nativeStr) +{ + return emitIntrinsicInst(getStringType(), kIROp_MakeString, 1, &nativeStr); +} - IRInst* IRBuilder::emitGetNativeString(IRInst* str) - { - return emitIntrinsicInst(getNativeStringType(), kIROp_getNativeStr, 1, &str); - } +IRInst* IRBuilder::emitGetNativeString(IRInst* str) +{ + return emitIntrinsicInst(getNativeStringType(), kIROp_getNativeStr, 1, &str); +} - IRInst* IRBuilder::emitGetTupleElement(IRType* type, IRInst* tuple, IRInst* element) - { - IRInst* args[] = { tuple, element }; - return emitIntrinsicInst(type, kIROp_GetTupleElement, 2, args); - } +IRInst* IRBuilder::emitGetTupleElement(IRType* type, IRInst* tuple, IRInst* element) +{ + IRInst* args[] = {tuple, element}; + return emitIntrinsicInst(type, kIROp_GetTupleElement, 2, args); +} - IRInst* IRBuilder::emitGetTupleElement(IRType* type, IRInst* tuple, UInt element) +IRInst* IRBuilder::emitGetTupleElement(IRType* type, IRInst* tuple, UInt element) +{ + // As a quick simplification/optimization, if the user requests + // `getTupleElement(makeTuple(a_0, a_1, ... a_N), i)` then we should + // just return `a_i`, provided that the index is properly in range. + // + switch (tuple->getOp()) { - // As a quick simplification/optimization, if the user requests - // `getTupleElement(makeTuple(a_0, a_1, ... a_N), i)` then we should - // just return `a_i`, provided that the index is properly in range. - // - switch(tuple->getOp()) + case kIROp_MakeTuple: + case kIROp_MakeValuePack: + case kIROp_MakeWitnessPack: + case kIROp_TypePack: + if (element < tuple->getOperandCount()) { - case kIROp_MakeTuple: - case kIROp_MakeValuePack: - case kIROp_MakeWitnessPack: - case kIROp_TypePack: - if( element < tuple->getOperandCount() ) - { - return tuple->getOperand(element); - } - break; + return tuple->getOperand(element); } - return emitGetTupleElement(type, tuple, getIntValue(getIntType(), element)); + break; } + return emitGetTupleElement(type, tuple, getIntValue(getIntType(), element)); +} - IRInst* IRBuilder::emitMakeResultError(IRType* resultType, IRInst* errorVal) - { - return emitIntrinsicInst(resultType, kIROp_MakeResultError, 1, &errorVal); - } - - IRInst* IRBuilder::emitMakeResultValue(IRType* resultType, IRInst* value) - { - return emitIntrinsicInst(resultType, kIROp_MakeResultValue, 1, &value); - } - - IRInst* IRBuilder::emitIsResultError(IRInst* result) - { - return emitIntrinsicInst(getBoolType(), kIROp_IsResultError, 1, &result); - } - - IRInst* IRBuilder::emitGetResultError(IRInst* result) - { - SLANG_ASSERT(result->getDataType()); - return emitIntrinsicInst( - cast<IRResultType>(result->getDataType())->getErrorType(), - kIROp_GetResultError, - 1, - &result); - } +IRInst* IRBuilder::emitMakeResultError(IRType* resultType, IRInst* errorVal) +{ + return emitIntrinsicInst(resultType, kIROp_MakeResultError, 1, &errorVal); +} - IRInst* IRBuilder::emitGetResultValue(IRInst* result) - { - SLANG_ASSERT(result->getDataType()); - return emitIntrinsicInst( - cast<IRResultType>(result->getDataType())->getValueType(), - kIROp_GetResultValue, - 1, - &result); - } +IRInst* IRBuilder::emitMakeResultValue(IRType* resultType, IRInst* value) +{ + return emitIntrinsicInst(resultType, kIROp_MakeResultValue, 1, &value); +} - IRInst* IRBuilder::emitOptionalHasValue(IRInst* optValue) - { - return emitIntrinsicInst( - getBoolType(), - kIROp_OptionalHasValue, - 1, - &optValue); - } +IRInst* IRBuilder::emitIsResultError(IRInst* result) +{ + return emitIntrinsicInst(getBoolType(), kIROp_IsResultError, 1, &result); +} - IRInst* IRBuilder::emitGetOptionalValue(IRInst* optValue) - { - return emitIntrinsicInst( - cast<IROptionalType>(optValue->getDataType())->getValueType(), - kIROp_GetOptionalValue, - 1, - &optValue); - } +IRInst* IRBuilder::emitGetResultError(IRInst* result) +{ + SLANG_ASSERT(result->getDataType()); + return emitIntrinsicInst( + cast<IRResultType>(result->getDataType())->getErrorType(), + kIROp_GetResultError, + 1, + &result); +} + +IRInst* IRBuilder::emitGetResultValue(IRInst* result) +{ + SLANG_ASSERT(result->getDataType()); + return emitIntrinsicInst( + cast<IRResultType>(result->getDataType())->getValueType(), + kIROp_GetResultValue, + 1, + &result); +} + +IRInst* IRBuilder::emitOptionalHasValue(IRInst* optValue) +{ + return emitIntrinsicInst(getBoolType(), kIROp_OptionalHasValue, 1, &optValue); +} - IRInst* IRBuilder::emitMakeOptionalValue(IRInst* optType, IRInst* value) - { - return emitIntrinsicInst( - (IRType*)optType, - kIROp_MakeOptionalValue, - 1, - &value); - } +IRInst* IRBuilder::emitGetOptionalValue(IRInst* optValue) +{ + return emitIntrinsicInst( + cast<IROptionalType>(optValue->getDataType())->getValueType(), + kIROp_GetOptionalValue, + 1, + &optValue); +} + +IRInst* IRBuilder::emitMakeOptionalValue(IRInst* optType, IRInst* value) +{ + return emitIntrinsicInst((IRType*)optType, kIROp_MakeOptionalValue, 1, &value); +} - IRInst* IRBuilder::emitMakeOptionalNone(IRInst* optType, IRInst* defaultValue) - { - return emitIntrinsicInst( - (IRType*)optType, - kIROp_MakeOptionalNone, - 1, - &defaultValue); - } +IRInst* IRBuilder::emitMakeOptionalNone(IRInst* optType, IRInst* defaultValue) +{ + return emitIntrinsicInst((IRType*)optType, kIROp_MakeOptionalNone, 1, &defaultValue); +} - IRInst* IRBuilder::emitMakeVectorFromScalar( - IRType* type, - IRInst* scalarValue) - { - return emitIntrinsicInst(type, kIROp_MakeVectorFromScalar, 1, &scalarValue); - } +IRInst* IRBuilder::emitMakeVectorFromScalar(IRType* type, IRInst* scalarValue) +{ + return emitIntrinsicInst(type, kIROp_MakeVectorFromScalar, 1, &scalarValue); +} - IRInst* IRBuilder::emitMatrixReshape(IRType* type, IRInst* inst) - { - return emitIntrinsicInst(type, kIROp_MatrixReshape, 1, &inst); - } +IRInst* IRBuilder::emitMatrixReshape(IRType* type, IRInst* inst) +{ + return emitIntrinsicInst(type, kIROp_MatrixReshape, 1, &inst); +} - IRInst* IRBuilder::emitMakeVector( - IRType* type, - UInt argCount, - IRInst* const* args) - { - return emitIntrinsicInst(type, kIROp_MakeVector, argCount, args); - } +IRInst* IRBuilder::emitMakeVector(IRType* type, UInt argCount, IRInst* const* args) +{ + return emitIntrinsicInst(type, kIROp_MakeVector, argCount, args); +} - IRInst* IRBuilder::emitDifferentialValuePairGetDifferential(IRType* diffType, IRInst* diffPair) - { - SLANG_ASSERT(as<IRDifferentialPairTypeBase>(diffPair->getDataType())); - return emitIntrinsicInst( - diffType, - kIROp_DifferentialPairGetDifferential, - 1, - &diffPair); - } +IRInst* IRBuilder::emitDifferentialValuePairGetDifferential(IRType* diffType, IRInst* diffPair) +{ + SLANG_ASSERT(as<IRDifferentialPairTypeBase>(diffPair->getDataType())); + return emitIntrinsicInst(diffType, kIROp_DifferentialPairGetDifferential, 1, &diffPair); +} - - IRInst* IRBuilder::emitDifferentialPtrPairGetDifferential(IRType* diffType, IRInst* diffPair) - { - SLANG_ASSERT(as<IRDifferentialPtrPairType>(diffPair->getDataType())); - return emitIntrinsicInst( - diffType, - kIROp_DifferentialPtrPairGetDifferential, - 1, - &diffPair); - } - IRInst* IRBuilder::emitDifferentialValuePairGetPrimal(IRInst* diffPair) - { - auto valueType = cast<IRDifferentialPairTypeBase>(diffPair->getDataType())->getValueType(); - return emitIntrinsicInst( - valueType, - kIROp_DifferentialPairGetPrimal, - 1, - &diffPair); - } +IRInst* IRBuilder::emitDifferentialPtrPairGetDifferential(IRType* diffType, IRInst* diffPair) +{ + SLANG_ASSERT(as<IRDifferentialPtrPairType>(diffPair->getDataType())); + return emitIntrinsicInst(diffType, kIROp_DifferentialPtrPairGetDifferential, 1, &diffPair); +} - IRInst* IRBuilder::emitDifferentialValuePairGetPrimal(IRType* primalType, IRInst* diffPair) - { - return emitIntrinsicInst( - primalType, - kIROp_DifferentialPairGetPrimal, - 1, - &diffPair); - } +IRInst* IRBuilder::emitDifferentialValuePairGetPrimal(IRInst* diffPair) +{ + auto valueType = cast<IRDifferentialPairTypeBase>(diffPair->getDataType())->getValueType(); + return emitIntrinsicInst(valueType, kIROp_DifferentialPairGetPrimal, 1, &diffPair); +} - IRInst* IRBuilder::emitDifferentialPtrPairGetPrimal(IRInst* diffPair) - { - auto valueType = cast<IRDifferentialPairTypeBase>(diffPair->getDataType())->getValueType(); - return emitIntrinsicInst( - valueType, - kIROp_DifferentialPtrPairGetPrimal, - 1, - &diffPair); - } +IRInst* IRBuilder::emitDifferentialValuePairGetPrimal(IRType* primalType, IRInst* diffPair) +{ + return emitIntrinsicInst(primalType, kIROp_DifferentialPairGetPrimal, 1, &diffPair); +} - IRInst* IRBuilder::emitDifferentialPtrPairGetPrimal(IRType* primalType, IRInst* diffPair) - { - return emitIntrinsicInst( - primalType, - kIROp_DifferentialPtrPairGetPrimal, - 1, - &diffPair); - } +IRInst* IRBuilder::emitDifferentialPtrPairGetPrimal(IRInst* diffPair) +{ + auto valueType = cast<IRDifferentialPairTypeBase>(diffPair->getDataType())->getValueType(); + return emitIntrinsicInst(valueType, kIROp_DifferentialPtrPairGetPrimal, 1, &diffPair); +} - IRInst* IRBuilder::emitDifferentialPairGetDifferentialUserCode(IRType* diffType, IRInst* diffPair) - { - SLANG_ASSERT(as<IRDifferentialPairTypeBase>(diffPair->getDataType())); - return emitIntrinsicInst( - diffType, - kIROp_DifferentialPairGetDifferentialUserCode, - 1, - &diffPair); - } +IRInst* IRBuilder::emitDifferentialPtrPairGetPrimal(IRType* primalType, IRInst* diffPair) +{ + return emitIntrinsicInst(primalType, kIROp_DifferentialPtrPairGetPrimal, 1, &diffPair); +} - IRInst* IRBuilder::emitDifferentialPairGetPrimalUserCode(IRInst* diffPair) - { - auto valueType = cast<IRDifferentialPairTypeBase>(diffPair->getDataType())->getValueType(); - return emitIntrinsicInst( - valueType, - kIROp_DifferentialPairGetPrimalUserCode, - 1, - &diffPair); - } +IRInst* IRBuilder::emitDifferentialPairGetDifferentialUserCode(IRType* diffType, IRInst* diffPair) +{ + SLANG_ASSERT(as<IRDifferentialPairTypeBase>(diffPair->getDataType())); + return emitIntrinsicInst(diffType, kIROp_DifferentialPairGetDifferentialUserCode, 1, &diffPair); +} - IRInst* IRBuilder::emitMakeMatrix( - IRType* type, - UInt argCount, - IRInst* const* args) - { - return emitIntrinsicInst(type, kIROp_MakeMatrix, argCount, args); - } +IRInst* IRBuilder::emitDifferentialPairGetPrimalUserCode(IRInst* diffPair) +{ + auto valueType = cast<IRDifferentialPairTypeBase>(diffPair->getDataType())->getValueType(); + return emitIntrinsicInst(valueType, kIROp_DifferentialPairGetPrimalUserCode, 1, &diffPair); +} - IRInst* IRBuilder::emitMakeMatrixFromScalar( - IRType* type, - IRInst* scalarValue) - { - return emitIntrinsicInst(type, kIROp_MakeMatrixFromScalar, 1, &scalarValue); - } +IRInst* IRBuilder::emitMakeMatrix(IRType* type, UInt argCount, IRInst* const* args) +{ + return emitIntrinsicInst(type, kIROp_MakeMatrix, argCount, args); +} - IRInst* IRBuilder::emitMakeArray( - IRType* type, - UInt argCount, - IRInst* const* args) - { - return emitIntrinsicInst(type, kIROp_MakeArray, argCount, args); - } +IRInst* IRBuilder::emitMakeMatrixFromScalar(IRType* type, IRInst* scalarValue) +{ + return emitIntrinsicInst(type, kIROp_MakeMatrixFromScalar, 1, &scalarValue); +} - IRInst* IRBuilder::emitMakeArrayList(IRType* type, UInt argCount, IRInst* const* args) - { - return emitIntrinsicInst(type, kIROp_MakeArrayList, argCount, args); - } +IRInst* IRBuilder::emitMakeArray(IRType* type, UInt argCount, IRInst* const* args) +{ + return emitIntrinsicInst(type, kIROp_MakeArray, argCount, args); +} - IRInst* IRBuilder::emitMakeArrayFromElement( - IRType* type, - IRInst* element) - { - return emitIntrinsicInst(type, kIROp_MakeArrayFromElement, 1, &element); - } +IRInst* IRBuilder::emitMakeArrayList(IRType* type, UInt argCount, IRInst* const* args) +{ + return emitIntrinsicInst(type, kIROp_MakeArrayList, argCount, args); +} - IRInst* IRBuilder::emitMakeStruct( - IRType* type, - UInt argCount, - IRInst* const* args) - { - return emitIntrinsicInst(type, kIROp_MakeStruct, argCount, args); - } +IRInst* IRBuilder::emitMakeArrayFromElement(IRType* type, IRInst* element) +{ + return emitIntrinsicInst(type, kIROp_MakeArrayFromElement, 1, &element); +} - IRInst* IRBuilder::emitMakeTensorView(IRType* type, IRInst* val) - { - return emitIntrinsicInst(type, kIROp_MakeTensorView, 1, &val); - } +IRInst* IRBuilder::emitMakeStruct(IRType* type, UInt argCount, IRInst* const* args) +{ + return emitIntrinsicInst(type, kIROp_MakeStruct, argCount, args); +} - IRInst* IRBuilder::emitMakeExistential( - IRType* type, - IRInst* value, - IRInst* witnessTable) - { - IRInst* args[] = {value, witnessTable}; - return emitIntrinsicInst(type, kIROp_MakeExistential, SLANG_COUNT_OF(args), args); - } +IRInst* IRBuilder::emitMakeTensorView(IRType* type, IRInst* val) +{ + return emitIntrinsicInst(type, kIROp_MakeTensorView, 1, &val); +} - IRInst* IRBuilder::emitMakeExistentialWithRTTI( - IRType* type, - IRInst* value, - IRInst* witnessTable, - IRInst* rtti) - { - IRInst* args[] = { value, witnessTable, rtti }; - return emitIntrinsicInst(type, kIROp_MakeExistentialWithRTTI, SLANG_COUNT_OF(args), args); - } +IRInst* IRBuilder::emitMakeExistential(IRType* type, IRInst* value, IRInst* witnessTable) +{ + IRInst* args[] = {value, witnessTable}; + return emitIntrinsicInst(type, kIROp_MakeExistential, SLANG_COUNT_OF(args), args); +} + +IRInst* IRBuilder::emitMakeExistentialWithRTTI( + IRType* type, + IRInst* value, + IRInst* witnessTable, + IRInst* rtti) +{ + IRInst* args[] = {value, witnessTable, rtti}; + return emitIntrinsicInst(type, kIROp_MakeExistentialWithRTTI, SLANG_COUNT_OF(args), args); +} + +IRInst* IRBuilder::emitWrapExistential( + IRType* type, + IRInst* value, + UInt slotArgCount, + IRInst* const* slotArgs) +{ + if (slotArgCount == 0) + return value; - IRInst* IRBuilder::emitWrapExistential( - IRType* type, - IRInst* value, - UInt slotArgCount, - IRInst* const* slotArgs) + // If we are wrapping a single concrete value into + // an interface type, then this is really a `makeExistential` + // + // TODO: We may want to check for a `specialize` of a generic interface as well. + // + if (as<IRInterfaceType>(type)) { - if(slotArgCount == 0) - return value; - - // If we are wrapping a single concrete value into - // an interface type, then this is really a `makeExistential` - // - // TODO: We may want to check for a `specialize` of a generic interface as well. - // - if(as<IRInterfaceType>(type)) + if (slotArgCount >= 2) { - if(slotArgCount >= 2) - { - // We are being asked to emit `wrapExistential(value, concreteType, witnessTable, ...) : someInterface` - // - // We also know that a concrete value being wrapped will always be an existential box, - // so we expect that `value : BindInterface<I, C>` for some concrete `C`. - // - // We want to emit `makeExistential(getValueFromBoundInterface(value) : C, witnessTable)`. - // - auto concreteType = (IRType*)(slotArgs[0]); - auto witnessTable = slotArgs[1]; - if (slotArgs[0]->getOp() == kIROp_DynamicType) - return value; - auto deref = emitGetValueFromBoundInterface(concreteType, value); - return emitMakeExistential(type, deref, witnessTable); - } - } - - IRInst* fixedArgs[] = {value}; - auto inst = createInstImpl<IRInst>( - this, - kIROp_WrapExistential, - type, - SLANG_COUNT_OF(fixedArgs), - fixedArgs, - slotArgCount, - slotArgs); - addInst(inst); - return inst; - } - - IRInst* IRBuilder::addPrimalValueStructKeyDecoration(IRInst* target, IRStructKey* key) - { - return addDecoration(target, kIROp_PrimalValueStructKeyDecoration, key); - } + // We are being asked to emit `wrapExistential(value, concreteType, witnessTable, ...) : + // someInterface` + // + // We also know that a concrete value being wrapped will always be an existential box, + // so we expect that `value : BindInterface<I, C>` for some concrete `C`. + // + // We want to emit `makeExistential(getValueFromBoundInterface(value) : C, + // witnessTable)`. + // + auto concreteType = (IRType*)(slotArgs[0]); + auto witnessTable = slotArgs[1]; + if (slotArgs[0]->getOp() == kIROp_DynamicType) + return value; + auto deref = emitGetValueFromBoundInterface(concreteType, value); + return emitMakeExistential(type, deref, witnessTable); + } + } + + IRInst* fixedArgs[] = {value}; + auto inst = createInstImpl<IRInst>( + this, + kIROp_WrapExistential, + type, + SLANG_COUNT_OF(fixedArgs), + fixedArgs, + slotArgCount, + slotArgs); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::addPrimalValueStructKeyDecoration(IRInst* target, IRStructKey* key) +{ + return addDecoration(target, kIROp_PrimalValueStructKeyDecoration, key); +} - IRInst* IRBuilder::addPrimalElementTypeDecoration(IRInst* target, IRInst* type) - { - return addDecoration(target, kIROp_PrimalElementTypeDecoration, type); - } +IRInst* IRBuilder::addPrimalElementTypeDecoration(IRInst* target, IRInst* type) +{ + return addDecoration(target, kIROp_PrimalElementTypeDecoration, type); +} - IRInst* IRBuilder::addIntermediateContextFieldDifferentialTypeDecoration(IRInst* target, IRInst* witness) - { - return addDecoration(target, kIROp_IntermediateContextFieldDifferentialTypeDecoration, witness); - } +IRInst* IRBuilder::addIntermediateContextFieldDifferentialTypeDecoration( + IRInst* target, + IRInst* witness) +{ + return addDecoration(target, kIROp_IntermediateContextFieldDifferentialTypeDecoration, witness); +} - RefPtr<IRModule> IRModule::create(Session* session) - { - RefPtr<IRModule> module = new IRModule(session); +RefPtr<IRModule> IRModule::create(Session* session) +{ + RefPtr<IRModule> module = new IRModule(session); - auto moduleInst = module->_allocateInst<IRModuleInst>(kIROp_Module, 0); + auto moduleInst = module->_allocateInst<IRModuleInst>(kIROp_Module, 0); - module->m_moduleInst = moduleInst; - moduleInst->module = module; + module->m_moduleInst = moduleInst; + moduleInst->module = module; - return module; - } + return module; +} - IRDominatorTree* IRModule::findOrCreateDominatorTree(IRGlobalValueWithCode* func) - { - IRAnalysis* analysis = m_mapInstToAnalysis.tryGetValue(func); - if (analysis) - return analysis->getDominatorTree(); - else - { - m_mapInstToAnalysis[func] = IRAnalysis(); - analysis = m_mapInstToAnalysis.tryGetValue(func); - } - analysis->domTree = computeDominatorTree(func); +IRDominatorTree* IRModule::findOrCreateDominatorTree(IRGlobalValueWithCode* func) +{ + IRAnalysis* analysis = m_mapInstToAnalysis.tryGetValue(func); + if (analysis) return analysis->getDominatorTree(); - } - - void addGlobalValue( - IRBuilder* builder, - IRInst* value) + else { - // Try to find a suitable parent for the - // global value we are emitting. - // - // We will start out search at the current - // parent instruction for the builder, and - // possibly work our way up. - // - auto defaultInsertLoc = builder->getInsertLoc(); - auto defaultParent = defaultInsertLoc.getParent(); - auto parent = defaultParent; - while(parent) - { - // Inserting into the top level of a module? - // That is fine, and we can stop searching. - if (as<IRModuleInst>(parent)) - break; - - // Inserting into a basic block inside of - // a generic? That is okay too. - if (auto block = as<IRBlock>(parent)) - { - if (as<IRGeneric>(block->parent)) - break; - } + m_mapInstToAnalysis[func] = IRAnalysis(); + analysis = m_mapInstToAnalysis.tryGetValue(func); + } + analysis->domTree = computeDominatorTree(func); + return analysis->getDominatorTree(); +} - // Otherwise, move up the chain. - parent = parent->parent; - } +void addGlobalValue(IRBuilder* builder, IRInst* value) +{ + // Try to find a suitable parent for the + // global value we are emitting. + // + // We will start out search at the current + // parent instruction for the builder, and + // possibly work our way up. + // + auto defaultInsertLoc = builder->getInsertLoc(); + auto defaultParent = defaultInsertLoc.getParent(); + auto parent = defaultParent; + while (parent) + { + // Inserting into the top level of a module? + // That is fine, and we can stop searching. + if (as<IRModuleInst>(parent)) + break; - // If we somehow ran out of parents (possibly - // because an instruction wasn't linked into - // the full hierarchy yet), then we will - // fall back to inserting into the overall module. - if (!parent) + // Inserting into a basic block inside of + // a generic? That is okay too. + if (auto block = as<IRBlock>(parent)) { - parent = builder->getModule()->getModuleInst(); + if (as<IRGeneric>(block->parent)) + break; } - // If it turns out that we are inserting into the - // current "insert into" parent for the builder, then - // we need to respect its "insert before" setting - // as well. - if (parent == defaultParent) - { - value->insertAt(defaultInsertLoc); - } - else - { - value->insertAtEnd(parent); - } + // Otherwise, move up the chain. + parent = parent->parent; } - IRInst* IRBuilder::addDifferentiableTypeDictionaryDecoration(IRInst* target) + // If we somehow ran out of parents (possibly + // because an instruction wasn't linked into + // the full hierarchy yet), then we will + // fall back to inserting into the overall module. + if (!parent) { - return addDecoration(target, kIROp_DifferentiableTypeDictionaryDecoration); + parent = builder->getModule()->getModuleInst(); } - IRInst* IRBuilder::addDifferentiableTypeEntry(IRInst* dictDecoration, IRInst* irType, IRInst* conformanceWitness) + // If it turns out that we are inserting into the + // current "insert into" parent for the builder, then + // we need to respect its "insert before" setting + // as well. + if (parent == defaultParent) { - auto oldLoc = this->getInsertLoc(); - - IRDifferentiableTypeDictionaryItem* item = nullptr; - - this->setInsertInto(dictDecoration); - - IRInst* args[2] = {irType, conformanceWitness}; - item = createInstWithTrailingArgs<IRDifferentiableTypeDictionaryItem>( - this, - kIROp_DifferentiableTypeDictionaryItem, - nullptr, - 2, - args); - - addInst(item); - - this->setInsertLoc(oldLoc); - - return item; + value->insertAt(defaultInsertLoc); } - - - IRFunc* IRBuilder::createFunc() + else { - IRFunc* rsFunc = createInst<IRFunc>( - this, - kIROp_Func, - nullptr); - _maybeSetSourceLoc(rsFunc); - addGlobalValue(this, rsFunc); - return rsFunc; + value->insertAtEnd(parent); } +} - IRGlobalVar* IRBuilder::createGlobalVar( - IRType* valueType) - { - auto ptrType = getPtrType(valueType); - IRGlobalVar* globalVar = createInst<IRGlobalVar>( - this, - kIROp_GlobalVar, - ptrType); - _maybeSetSourceLoc(globalVar); - addGlobalValue(this, globalVar); - return globalVar; - } - - IRGlobalVar* IRBuilder::createGlobalVar( - IRType* valueType, - AddressSpace addressSpace) - { - auto ptrType = getPtrType(kIROp_PtrType, valueType, addressSpace); - IRGlobalVar* globalVar = createInst<IRGlobalVar>( - this, - kIROp_GlobalVar, - ptrType); - _maybeSetSourceLoc(globalVar); - addGlobalValue(this, globalVar); - return globalVar; - } - - IRGlobalParam* IRBuilder::createGlobalParam( - IRType* valueType) - { - IRGlobalParam* inst = createInst<IRGlobalParam>( - this, - kIROp_GlobalParam, - valueType); - _maybeSetSourceLoc(inst); - addGlobalValue(this, inst); - return inst; - } +IRInst* IRBuilder::addDifferentiableTypeDictionaryDecoration(IRInst* target) +{ + return addDecoration(target, kIROp_DifferentiableTypeDictionaryDecoration); +} - IRWitnessTable* IRBuilder::createWitnessTable(IRType* baseType, IRType* subType) - { - IRWitnessTable* witnessTable = createInst<IRWitnessTable>( - this, - kIROp_WitnessTable, - getWitnessTableType(baseType), - subType); - addGlobalValue(this, witnessTable); - return witnessTable; - } +IRInst* IRBuilder::addDifferentiableTypeEntry( + IRInst* dictDecoration, + IRInst* irType, + IRInst* conformanceWitness) +{ + auto oldLoc = this->getInsertLoc(); - IRWitnessTableEntry* IRBuilder::createWitnessTableEntry( - IRWitnessTable* witnessTable, - IRInst* requirementKey, - IRInst* satisfyingVal) - { - IRWitnessTableEntry* entry = createInst<IRWitnessTableEntry>( - this, - kIROp_WitnessTableEntry, - nullptr, - requirementKey, - satisfyingVal); + IRDifferentiableTypeDictionaryItem* item = nullptr; - if (witnessTable) - { - entry->insertAtEnd(witnessTable); - } + this->setInsertInto(dictDecoration); - return entry; - } + IRInst* args[2] = {irType, conformanceWitness}; + item = createInstWithTrailingArgs<IRDifferentiableTypeDictionaryItem>( + this, + kIROp_DifferentiableTypeDictionaryItem, + nullptr, + 2, + args); - IRInterfaceRequirementEntry* IRBuilder::createInterfaceRequirementEntry( - IRInst* requirementKey, - IRInst* requirementVal) - { - IRInterfaceRequirementEntry* entry = createInst<IRInterfaceRequirementEntry>( - this, - kIROp_InterfaceRequirementEntry, - nullptr, - requirementKey, - requirementVal); - addGlobalValue(this, entry); - return entry; - } + addInst(item); - IRInst* IRBuilder::createThisTypeWitness(IRType* interfaceType) - { - IRInst* witness = createInst<IRThisTypeWitness>( - this, - kIROp_ThisTypeWitness, - getWitnessTableType(interfaceType)); - addGlobalValue(this, witness); - return witness; - } + this->setInsertLoc(oldLoc); - IRInst* IRBuilder::getTypeEqualityWitness(IRType* witnessType, IRType* type1, IRType* type2) - { - IRInst* operands[2] = { type1, type2 }; - return (IRType*)createIntrinsicInst( - witnessType, - kIROp_TypeEqualityWitness, - 2, - operands); - } + return item; +} - IRStructType* IRBuilder::createStructType() - { - IRStructType* structType = createInst<IRStructType>( - this, - kIROp_StructType, - getTypeKind()); - addGlobalValue(this, structType); - return structType; - } - IRClassType* IRBuilder::createClassType() - { - IRClassType* classType = createInst<IRClassType>( - this, - kIROp_ClassType, - getTypeKind()); - addGlobalValue(this, classType); - return classType; - } +IRFunc* IRBuilder::createFunc() +{ + IRFunc* rsFunc = createInst<IRFunc>(this, kIROp_Func, nullptr); + _maybeSetSourceLoc(rsFunc); + addGlobalValue(this, rsFunc); + return rsFunc; +} - IRGLSLShaderStorageBufferType* IRBuilder::createGLSLShaderStorableBufferType() - { - IRGLSLShaderStorageBufferType* ssboType = createInst<IRGLSLShaderStorageBufferType>( - this, - kIROp_GLSLShaderStorageBufferType, - getTypeKind()); - addGlobalValue(this, ssboType); - return ssboType; - } +IRGlobalVar* IRBuilder::createGlobalVar(IRType* valueType) +{ + auto ptrType = getPtrType(valueType); + IRGlobalVar* globalVar = createInst<IRGlobalVar>(this, kIROp_GlobalVar, ptrType); + _maybeSetSourceLoc(globalVar); + addGlobalValue(this, globalVar); + return globalVar; +} + +IRGlobalVar* IRBuilder::createGlobalVar(IRType* valueType, AddressSpace addressSpace) +{ + auto ptrType = getPtrType(kIROp_PtrType, valueType, addressSpace); + IRGlobalVar* globalVar = createInst<IRGlobalVar>(this, kIROp_GlobalVar, ptrType); + _maybeSetSourceLoc(globalVar); + addGlobalValue(this, globalVar); + return globalVar; +} + +IRGlobalParam* IRBuilder::createGlobalParam(IRType* valueType) +{ + IRGlobalParam* inst = createInst<IRGlobalParam>(this, kIROp_GlobalParam, valueType); + _maybeSetSourceLoc(inst); + addGlobalValue(this, inst); + return inst; +} - IRGLSLShaderStorageBufferType* IRBuilder::createGLSLShaderStorableBufferType(UInt operandCount, IRInst* const* operands) - { - IRGLSLShaderStorageBufferType* ssboType = createInst<IRGLSLShaderStorageBufferType>( - this, - kIROp_GLSLShaderStorageBufferType, - getTypeKind(), - operandCount, - operands); - addGlobalValue(this, ssboType); - return ssboType; - } +IRWitnessTable* IRBuilder::createWitnessTable(IRType* baseType, IRType* subType) +{ + IRWitnessTable* witnessTable = createInst<IRWitnessTable>( + this, + kIROp_WitnessTable, + getWitnessTableType(baseType), + subType); + addGlobalValue(this, witnessTable); + return witnessTable; +} + +IRWitnessTableEntry* IRBuilder::createWitnessTableEntry( + IRWitnessTable* witnessTable, + IRInst* requirementKey, + IRInst* satisfyingVal) +{ + IRWitnessTableEntry* entry = createInst<IRWitnessTableEntry>( + this, + kIROp_WitnessTableEntry, + nullptr, + requirementKey, + satisfyingVal); - IRInterfaceType* IRBuilder::createInterfaceType(UInt operandCount, IRInst* const* operands) + if (witnessTable) { - IRInterfaceType* interfaceType = createInst<IRInterfaceType>( - this, - kIROp_InterfaceType, - getTypeKind(), - operandCount, - operands); - addGlobalValue(this, interfaceType); - return interfaceType; + entry->insertAtEnd(witnessTable); } - IRStructKey* IRBuilder::createStructKey() - { - IRStructKey* structKey = createInst<IRStructKey>( - this, - kIROp_StructKey, - nullptr); - addGlobalValue(this, structKey); - return structKey; - } + return entry; +} - // Create a field nested in a struct type, declaring that - // the specified field key maps to a field with the specified type. - IRStructField* IRBuilder::createStructField( - IRType* aggType, - IRStructKey* fieldKey, - IRType* fieldType) - { - IRInst* operands[] = { fieldKey, fieldType }; - IRStructField* field = (IRStructField*) createInstWithTrailingArgs<IRInst>( - this, - kIROp_StructField, - nullptr, - 0, - nullptr, - 2, - operands); +IRInterfaceRequirementEntry* IRBuilder::createInterfaceRequirementEntry( + IRInst* requirementKey, + IRInst* requirementVal) +{ + IRInterfaceRequirementEntry* entry = createInst<IRInterfaceRequirementEntry>( + this, + kIROp_InterfaceRequirementEntry, + nullptr, + requirementKey, + requirementVal); + addGlobalValue(this, entry); + return entry; +} + +IRInst* IRBuilder::createThisTypeWitness(IRType* interfaceType) +{ + IRInst* witness = createInst<IRThisTypeWitness>( + this, + kIROp_ThisTypeWitness, + getWitnessTableType(interfaceType)); + addGlobalValue(this, witness); + return witness; +} + +IRInst* IRBuilder::getTypeEqualityWitness(IRType* witnessType, IRType* type1, IRType* type2) +{ + IRInst* operands[2] = {type1, type2}; + return (IRType*)createIntrinsicInst(witnessType, kIROp_TypeEqualityWitness, 2, operands); +} - if (aggType) - { - field->insertAtEnd(aggType); - } +IRStructType* IRBuilder::createStructType() +{ + IRStructType* structType = createInst<IRStructType>(this, kIROp_StructType, getTypeKind()); + addGlobalValue(this, structType); + return structType; +} - return field; - } +IRClassType* IRBuilder::createClassType() +{ + IRClassType* classType = createInst<IRClassType>(this, kIROp_ClassType, getTypeKind()); + addGlobalValue(this, classType); + return classType; +} - IRGeneric* IRBuilder::createGeneric() - { - IRGeneric* irGeneric = createInst<IRGeneric>( - this, - kIROp_Generic, - nullptr); - return irGeneric; - } +IRGLSLShaderStorageBufferType* IRBuilder::createGLSLShaderStorableBufferType() +{ + IRGLSLShaderStorageBufferType* ssboType = createInst<IRGLSLShaderStorageBufferType>( + this, + kIROp_GLSLShaderStorageBufferType, + getTypeKind()); + addGlobalValue(this, ssboType); + return ssboType; +} + +IRGLSLShaderStorageBufferType* IRBuilder::createGLSLShaderStorableBufferType( + UInt operandCount, + IRInst* const* operands) +{ + IRGLSLShaderStorageBufferType* ssboType = createInst<IRGLSLShaderStorageBufferType>( + this, + kIROp_GLSLShaderStorageBufferType, + getTypeKind(), + operandCount, + operands); + addGlobalValue(this, ssboType); + return ssboType; +} + +IRInterfaceType* IRBuilder::createInterfaceType(UInt operandCount, IRInst* const* operands) +{ + IRInterfaceType* interfaceType = createInst<IRInterfaceType>( + this, + kIROp_InterfaceType, + getTypeKind(), + operandCount, + operands); + addGlobalValue(this, interfaceType); + return interfaceType; +} + +IRStructKey* IRBuilder::createStructKey() +{ + IRStructKey* structKey = createInst<IRStructKey>(this, kIROp_StructKey, nullptr); + addGlobalValue(this, structKey); + return structKey; +} + +// Create a field nested in a struct type, declaring that +// the specified field key maps to a field with the specified type. +IRStructField* IRBuilder::createStructField( + IRType* aggType, + IRStructKey* fieldKey, + IRType* fieldType) +{ + IRInst* operands[] = {fieldKey, fieldType}; + IRStructField* field = (IRStructField*)createInstWithTrailingArgs<IRInst>( + this, + kIROp_StructField, + nullptr, + 0, + nullptr, + 2, + operands); - IRGeneric* IRBuilder::emitGeneric() + if (aggType) { - auto irGeneric = createGeneric(); - addGlobalValue(this, irGeneric); - return irGeneric; + field->insertAtEnd(aggType); } - IRBlock* IRBuilder::createBlock() - { - return createInst<IRBlock>( - this, - kIROp_Block, - getBasicBlockType()); - } + return field; +} - void IRBuilder::insertBlock(IRBlock* block) - { - // If we are emitting into a function - // (or another value with code), then - // append the block to the function and - // set this block as the new parent for - // subsequent instructions we insert. - // - // TODO: This should probably insert the block - // after the current "insert into" block if - // there is one. Right now we are always - // adding the block to the end of the list, - // which is technically valid (the ordering - // of blocks doesn't affect the CFG topology), - // but some later passes might assume the ordering - // is significant in representing the intent - // of the original code. - // - auto f = getFunc(); - if (f) - { - f->addBlock(block); - setInsertInto(block); - } - } +IRGeneric* IRBuilder::createGeneric() +{ + IRGeneric* irGeneric = createInst<IRGeneric>(this, kIROp_Generic, nullptr); + return irGeneric; +} - IRBlock* IRBuilder::emitBlock() - { - auto block = createBlock(); - insertBlock(block); - return block; - } +IRGeneric* IRBuilder::emitGeneric() +{ + auto irGeneric = createGeneric(); + addGlobalValue(this, irGeneric); + return irGeneric; +} - IRParam* IRBuilder::createParam( - IRType* type) - { - auto param = createInst<IRParam>( - this, - kIROp_Param, - type); - return param; - } +IRBlock* IRBuilder::createBlock() +{ + return createInst<IRBlock>(this, kIROp_Block, getBasicBlockType()); +} - IRParam* IRBuilder::emitParam( - IRType* type) +void IRBuilder::insertBlock(IRBlock* block) +{ + // If we are emitting into a function + // (or another value with code), then + // append the block to the function and + // set this block as the new parent for + // subsequent instructions we insert. + // + // TODO: This should probably insert the block + // after the current "insert into" block if + // there is one. Right now we are always + // adding the block to the end of the list, + // which is technically valid (the ordering + // of blocks doesn't affect the CFG topology), + // but some later passes might assume the ordering + // is significant in representing the intent + // of the original code. + // + auto f = getFunc(); + if (f) { - auto param = createParam(type); - if (auto bb = getBlock()) - { - bb->addParam(param); - } - return param; + f->addBlock(block); + setInsertInto(block); } +} - IRParam* IRBuilder::emitParamAtHead( - IRType* type) - { - auto param = createParam(type); - if (auto bb = getBlock()) - { - bb->insertParamAtHead(param); - } - return param; - } +IRBlock* IRBuilder::emitBlock() +{ + auto block = createBlock(); + insertBlock(block); + return block; +} - IRInst* IRBuilder::emitAllocObj(IRType* type) - { - return emitIntrinsicInst(type, kIROp_AllocObj, 0, nullptr); - } +IRParam* IRBuilder::createParam(IRType* type) +{ + auto param = createInst<IRParam>(this, kIROp_Param, type); + return param; +} - IRVar* IRBuilder::emitVar( - IRType* type) +IRParam* IRBuilder::emitParam(IRType* type) +{ + auto param = createParam(type); + if (auto bb = getBlock()) { - auto allocatedType = getPtrType(type); - auto inst = createInst<IRVar>( - this, - kIROp_Var, - allocatedType); - addInst(inst); - return inst; + bb->addParam(param); } + return param; +} - IRVar* IRBuilder::emitVar( - IRType* type, - AddressSpace addressSpace) +IRParam* IRBuilder::emitParamAtHead(IRType* type) +{ + auto param = createParam(type); + if (auto bb = getBlock()) { - auto allocatedType = getPtrType(kIROp_PtrType, type, addressSpace); - auto inst = createInst<IRVar>( - this, - kIROp_Var, - allocatedType); - addInst(inst); - return inst; + bb->insertParamAtHead(param); } + return param; +} - IRInst* IRBuilder::emitLoadReverseGradient(IRType* type, IRInst* diffValue) - { - auto inst = createInst<IRLoadReverseGradient>( - this, - kIROp_LoadReverseGradient, - type, - diffValue); +IRInst* IRBuilder::emitAllocObj(IRType* type) +{ + return emitIntrinsicInst(type, kIROp_AllocObj, 0, nullptr); +} - addInst(inst); - return inst; - } +IRVar* IRBuilder::emitVar(IRType* type) +{ + auto allocatedType = getPtrType(type); + auto inst = createInst<IRVar>(this, kIROp_Var, allocatedType); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitReverseGradientDiffPairRef(IRType* type, IRInst* primalVar, IRInst* diffVar) - { - auto inst = createInst<IRReverseGradientDiffPairRef>( - this, - kIROp_ReverseGradientDiffPairRef, - type, - primalVar, - diffVar); +IRVar* IRBuilder::emitVar(IRType* type, AddressSpace addressSpace) +{ + auto allocatedType = getPtrType(kIROp_PtrType, type, addressSpace); + auto inst = createInst<IRVar>(this, kIROp_Var, allocatedType); + addInst(inst); + return inst; +} - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitLoadReverseGradient(IRType* type, IRInst* diffValue) +{ + auto inst = createInst<IRLoadReverseGradient>(this, kIROp_LoadReverseGradient, type, diffValue); - IRInst* IRBuilder::emitPrimalParamRef(IRInst* param) - { - auto type = param->getFullType(); - auto ptrType = as<IRPtrTypeBase>(type); - auto valueType = type; - if (ptrType) valueType = ptrType->getValueType(); - auto pairType = as<IRDifferentialPairType>(valueType); - IRType* finalType = pairType->getValueType(); - if (ptrType) finalType = getPtrType(ptrType->getOp(), finalType); - auto inst = createInst<IRPrimalParamRef>( - this, - kIROp_PrimalParamRef, - finalType, - param); + addInst(inst); + return inst; +} - addInst(inst); - return inst; - } - - IRInst* IRBuilder::emitDiffParamRef(IRType* type, IRInst* param) - { - auto inst = createInst<IRDiffParamRef>( - this, - kIROp_DiffParamRef, - type, - param); +IRInst* IRBuilder::emitReverseGradientDiffPairRef(IRType* type, IRInst* primalVar, IRInst* diffVar) +{ + auto inst = createInst<IRReverseGradientDiffPairRef>( + this, + kIROp_ReverseGradientDiffPairRef, + type, + primalVar, + diffVar); + + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitPrimalParamRef(IRInst* param) +{ + auto type = param->getFullType(); + auto ptrType = as<IRPtrTypeBase>(type); + auto valueType = type; + if (ptrType) + valueType = ptrType->getValueType(); + auto pairType = as<IRDifferentialPairType>(valueType); + IRType* finalType = pairType->getValueType(); + if (ptrType) + finalType = getPtrType(ptrType->getOp(), finalType); + auto inst = createInst<IRPrimalParamRef>(this, kIROp_PrimalParamRef, finalType, param); + + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitDiffParamRef(IRType* type, IRInst* param) +{ + auto inst = createInst<IRDiffParamRef>(this, kIROp_DiffParamRef, type, param); - addInst(inst); - return inst; - } + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitLoad( - IRType* type, - IRInst* ptr) - { - auto inst = createInst<IRLoad>( - this, - kIROp_Load, - type, - ptr); +IRInst* IRBuilder::emitLoad(IRType* type, IRInst* ptr) +{ + auto inst = createInst<IRLoad>(this, kIROp_Load, type, ptr); - addInst(inst); - return inst; - } + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitLoad( - IRInst* ptr) +IRInst* IRBuilder::emitLoad(IRInst* ptr) +{ + // Note: a `load` operation does not consider the rate + // (if any) attached to its operand (see the use of `getDataType` + // below). This means that a load from a rate-qualified + // variable will still conceptually execute (and return + // results) at the "default" rate of the parent function, + // unless a subsequent analysis pass constraints it. + + IRType* valueType = tryGetPointedToType(this, ptr->getFullType()); + SLANG_ASSERT(valueType); + + // Ugly special case: if the front-end created a variable with + // type `Ptr<@R T>` instead of `@R Ptr<T>`, then the above + // logic will yield `@R T` instead of `T`, and we need to + // try and fix that up here. + // + // TODO: Lowering to the IR should be fixed to never create + // that case: rate-qualified types should only be allowed + // to appear as the type of an instruction, and should not + // be allowed as operands to type constructors (except + // in special cases we decide to allow). + // + if (auto rateType = as<IRRateQualifiedType>(valueType)) { - // Note: a `load` operation does not consider the rate - // (if any) attached to its operand (see the use of `getDataType` - // below). This means that a load from a rate-qualified - // variable will still conceptually execute (and return - // results) at the "default" rate of the parent function, - // unless a subsequent analysis pass constraints it. - - IRType* valueType = tryGetPointedToType(this, ptr->getFullType()); - SLANG_ASSERT(valueType); - - // Ugly special case: if the front-end created a variable with - // type `Ptr<@R T>` instead of `@R Ptr<T>`, then the above - // logic will yield `@R T` instead of `T`, and we need to - // try and fix that up here. - // - // TODO: Lowering to the IR should be fixed to never create - // that case: rate-qualified types should only be allowed - // to appear as the type of an instruction, and should not - // be allowed as operands to type constructors (except - // in special cases we decide to allow). - // - if(auto rateType = as<IRRateQualifiedType>(valueType)) - { - valueType = rateType->getValueType(); - } - - return emitLoad(valueType, ptr); + valueType = rateType->getValueType(); } - IRInst* IRBuilder::emitStore( - IRInst* dstPtr, - IRInst* srcVal) - { - auto inst = createInst<IRStore>( - this, - kIROp_Store, - nullptr, - dstPtr, - srcVal); + return emitLoad(valueType, ptr); +} - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitStore(IRInst* dstPtr, IRInst* srcVal) +{ + auto inst = createInst<IRStore>(this, kIROp_Store, nullptr, dstPtr, srcVal); - IRInst* IRBuilder::emitAtomicStore( - IRInst* dstPtr, - IRInst* srcVal, - IRInst* memoryOrder) - { - auto inst = createInst<IRAtomicStore>( - this, - kIROp_AtomicStore, - getVoidType(), - dstPtr, - srcVal, - memoryOrder); + addInst(inst); + return inst; +} - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitAtomicStore(IRInst* dstPtr, IRInst* srcVal, IRInst* memoryOrder) +{ + auto inst = createInst<IRAtomicStore>( + this, + kIROp_AtomicStore, + getVoidType(), + dstPtr, + srcVal, + memoryOrder); + + addInst(inst); + return inst; +} + +/// @param params An ordered list of imageLoad parameters { image, coord, [optional] +/// seperateArrayCoord, [optional] seperateSampleCoord } +IRInst* IRBuilder::emitImageLoad(IRType* type, ShortList<IRInst*> params) +{ + auto inst = createInst<IRImageLoad>( + this, + kIROp_ImageLoad, + type, + params.getCount(), + params.getArrayView().getBuffer()); + addInst(inst); + return inst; +} + +/// @param params An ordered list of imageStore parameters { image, coord, value, [optional] +/// seperateArrayCoord, [optional] seperateSampleCoord } +IRInst* IRBuilder::emitImageStore(IRType* type, ShortList<IRInst*> params) +{ + auto inst = createInst<IRImageStore>( + this, + kIROp_ImageStore, + type, + params.getCount(), + params.getArrayView().getBuffer()); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitIsType( + IRInst* value, + IRInst* witness, + IRInst* typeOperand, + IRInst* targetWitness) +{ + IRInst* args[] = {value, witness, typeOperand, targetWitness}; + auto inst = createInst<IRIsType>(this, kIROp_IsType, getBoolType(), SLANG_COUNT_OF(args), args); + addInst(inst); + return inst; +} - /// @param params An ordered list of imageLoad parameters { image, coord, [optional] seperateArrayCoord, [optional] seperateSampleCoord } - IRInst* IRBuilder::emitImageLoad(IRType* type, ShortList<IRInst*> params) - { - auto inst = createInst<IRImageLoad>(this, kIROp_ImageLoad, type, params.getCount(), params.getArrayView().getBuffer()); - addInst(inst); - return inst; +IRInst* IRBuilder::emitFieldExtract(IRInst* base, IRInst* fieldKey) +{ + IRType* resultType = nullptr; + auto valueType = base->getDataType(); + auto structType = as<IRStructType>(valueType); + SLANG_RELEASE_ASSERT(structType); + for (auto child : valueType->getChildren()) + { + auto field = as<IRStructField>(child); + if (!field) + continue; + if (field->getKey() == fieldKey) + { + resultType = field->getFieldType(); + break; + } } + SLANG_RELEASE_ASSERT(resultType); + return emitFieldExtract(resultType, base, fieldKey); +} - /// @param params An ordered list of imageStore parameters { image, coord, value, [optional] seperateArrayCoord, [optional] seperateSampleCoord } - IRInst* IRBuilder::emitImageStore(IRType* type, ShortList<IRInst*> params) - { - auto inst = createInst<IRImageStore>(this, kIROp_ImageStore, type, params.getCount(), params.getArrayView().getBuffer()); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitFieldExtract(IRType* type, IRInst* base, IRInst* field) +{ + auto inst = createInst<IRFieldExtract>(this, kIROp_FieldExtract, type, base, field); - IRInst* IRBuilder::emitIsType(IRInst* value, IRInst* witness, IRInst* typeOperand, IRInst* targetWitness) - { - IRInst* args[] = { value, witness, typeOperand, targetWitness }; - auto inst = createInst<IRIsType>(this, kIROp_IsType, getBoolType(), SLANG_COUNT_OF(args), args); - addInst(inst); - return inst; - } + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitFieldExtract(IRInst* base, IRInst* fieldKey) +IRType* maybePropagateAddressSpace(IRBuilder* builder, IRInst* basePtr, IRType* type) +{ + if (auto basePtrType = as<IRPtrTypeBase>(basePtr->getDataType())) { - IRType* resultType = nullptr; - auto valueType = base->getDataType(); - auto structType = as<IRStructType>(valueType); - SLANG_RELEASE_ASSERT(structType); - for (auto child : valueType->getChildren()) + if (auto resultPtrType = as<IRPtrTypeBase>(type)) { - auto field = as<IRStructField>(child); - if (!field) - continue; - if (field->getKey() == fieldKey) + if (basePtrType->getAddressSpace() != resultPtrType->getAddressSpace()) { - resultType = field->getFieldType(); - break; + type = builder->getPtrType( + resultPtrType->getOp(), + resultPtrType->getValueType(), + basePtrType->getAddressSpace()); } } - SLANG_RELEASE_ASSERT(resultType); - return emitFieldExtract(resultType, base, fieldKey); } + return type; +} - IRInst* IRBuilder::emitFieldExtract( - IRType* type, - IRInst* base, - IRInst* field) +IRInst* IRBuilder::emitFieldAddress(IRInst* basePtr, IRInst* fieldKey) +{ + AddressSpace addrSpace = AddressSpace::Generic; + IRInst* valueType = nullptr; + auto basePtrType = unwrapAttributedType(basePtr->getDataType()); + if (auto ptrType = as<IRPtrTypeBase>(basePtrType)) { - auto inst = createInst<IRFieldExtract>( - this, - kIROp_FieldExtract, - type, - base, - field); - - addInst(inst); - return inst; + addrSpace = ptrType->getAddressSpace(); + valueType = ptrType->getValueType(); } - - IRType* maybePropagateAddressSpace(IRBuilder* builder, IRInst* basePtr, IRType* type) + else if (auto ptrLikeType = as<IRPointerLikeType>(basePtrType)) { - if (auto basePtrType = as<IRPtrTypeBase>(basePtr->getDataType())) - { - if (auto resultPtrType = as<IRPtrTypeBase>(type)) - { - if (basePtrType->getAddressSpace() != resultPtrType->getAddressSpace()) - { - type = builder->getPtrType( - resultPtrType->getOp(), resultPtrType->getValueType(), basePtrType->getAddressSpace()); - } - } - } - return type; + valueType = ptrLikeType->getElementType(); } - - IRInst* IRBuilder::emitFieldAddress( - IRInst* basePtr, - IRInst* fieldKey) + IRType* resultType = nullptr; + auto structType = as<IRStructType>(valueType); + SLANG_RELEASE_ASSERT(structType); + for (auto child : valueType->getChildren()) { - AddressSpace addrSpace = AddressSpace::Generic; - IRInst* valueType = nullptr; - auto basePtrType = unwrapAttributedType(basePtr->getDataType()); - if (auto ptrType = as<IRPtrTypeBase>(basePtrType)) + auto field = as<IRStructField>(child); + if (!field) + continue; + if (field->getKey() == fieldKey) { - addrSpace = ptrType->getAddressSpace(); - valueType = ptrType->getValueType(); - } - else if (auto ptrLikeType = as<IRPointerLikeType>(basePtrType)) - { - valueType = ptrLikeType->getElementType(); - } - IRType* resultType = nullptr; - auto structType = as<IRStructType>(valueType); - SLANG_RELEASE_ASSERT(structType); - for (auto child : valueType->getChildren()) - { - auto field = as<IRStructField>(child); - if (!field) - continue; - if (field->getKey() == fieldKey) - { - resultType = field->getFieldType(); - break; - } + resultType = field->getFieldType(); + break; } - SLANG_RELEASE_ASSERT(resultType); - return emitFieldAddress(getPtrType(kIROp_PtrType, resultType, addrSpace), basePtr, fieldKey); } + SLANG_RELEASE_ASSERT(resultType); + return emitFieldAddress(getPtrType(kIROp_PtrType, resultType, addrSpace), basePtr, fieldKey); +} - IRInst* IRBuilder::emitFieldAddress( - IRType* type, - IRInst* base, - IRInst* field) - { - // Propagate pointer address space if it is available on base. - type = maybePropagateAddressSpace(this, base, type); +IRInst* IRBuilder::emitFieldAddress(IRType* type, IRInst* base, IRInst* field) +{ + // Propagate pointer address space if it is available on base. + type = maybePropagateAddressSpace(this, base, type); - auto inst = createInst<IRFieldAddress>( - this, - kIROp_FieldAddress, - type, - base, - field); + auto inst = createInst<IRFieldAddress>(this, kIROp_FieldAddress, type, base, field); - addInst(inst); - return inst; - } + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitElementExtract( - IRType* type, - IRInst* base, - IRInst* index) - { - if (auto vectorFromScalar = as<IRMakeVectorFromScalar>(base)) - return vectorFromScalar->getOperand(0); - if (base->getOp() == kIROp_MakeArrayFromElement) - return base->getOperand(0); +IRInst* IRBuilder::emitElementExtract(IRType* type, IRInst* base, IRInst* index) +{ + if (auto vectorFromScalar = as<IRMakeVectorFromScalar>(base)) + return vectorFromScalar->getOperand(0); + if (base->getOp() == kIROp_MakeArrayFromElement) + return base->getOperand(0); - auto inst = createInst<IRGetElement>( - this, - kIROp_GetElement, - type, - base, - index); + auto inst = createInst<IRGetElement>(this, kIROp_GetElement, type, base, index); - addInst(inst); - return inst; - } + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitElementExtract( - IRInst* base, - IRInst* index) +IRInst* IRBuilder::emitElementExtract(IRInst* base, IRInst* index) +{ + IRType* type = nullptr; + if (auto arrayType = as<IRArrayType>(base->getDataType())) { - IRType* type = nullptr; - if (auto arrayType = as<IRArrayType>(base->getDataType())) - { - type = arrayType->getElementType(); - } - else if (auto vectorType = as<IRVectorType>(base->getDataType())) - { - type = vectorType->getElementType(); - } - else if (auto matrixType = as<IRMatrixType>(base->getDataType())) - { - type = getVectorType(matrixType->getElementType(), matrixType->getColumnCount()); - } - else if (auto tupleType = as<IRTupleType>(base->getDataType())) - { - type = (IRType*)tupleType->getOperand(getIntVal(index)); - return emitGetTupleElement(type, base, index); - } - SLANG_RELEASE_ASSERT(type); - - return emitElementExtract(type, base, index); + type = arrayType->getElementType(); } - - IRInst* IRBuilder::emitElementExtract( - IRInst* base, - IRIntegerValue index) + else if (auto vectorType = as<IRVectorType>(base->getDataType())) { - return emitElementExtract(base, getIntValue(getIntType(), index)); + type = vectorType->getElementType(); } - - IRInst* IRBuilder::emitElementExtract( - IRInst* base, - const ArrayView<IRInst*>& accessChain) + else if (auto matrixType = as<IRMatrixType>(base->getDataType())) { - for (auto access : accessChain) - { - IRType* resultType = nullptr; - if (auto structKey = as<IRStructKey>(access)) - { - auto structType = as<IRStructType>(base->getDataType()); - SLANG_RELEASE_ASSERT(structType); - for (auto field : structType->getFields()) - { - if (field->getKey() == structKey) - { - resultType = field->getFieldType(); - break; - } - } - SLANG_RELEASE_ASSERT(resultType); - base = emitFieldExtract(resultType, base, structKey); - } - else - { - base = emitElementExtract(base, access); - } - } - return base; + type = getVectorType(matrixType->getElementType(), matrixType->getColumnCount()); } - - IRInst* IRBuilder::emitElementAddress( - IRType* type, - IRInst* basePtr, - IRInst* index) + else if (auto tupleType = as<IRTupleType>(base->getDataType())) { - // Propagate pointer address space if it is available on base. - type = maybePropagateAddressSpace(this, basePtr, type); - - auto inst = createInst<IRFieldAddress>( - this, - kIROp_GetElementPtr, - type, - basePtr, - index); - - addInst(inst); - return inst; + type = (IRType*)tupleType->getOperand(getIntVal(index)); + return emitGetTupleElement(type, base, index); } + SLANG_RELEASE_ASSERT(type); - IRInst* IRBuilder::emitElementAddress( - IRInst* basePtr, - IRIntegerValue index) - { - return emitElementAddress(basePtr, getIntValue(getIntType(), index)); - } + return emitElementExtract(type, base, index); +} - IRInst* IRBuilder::emitElementAddress( - IRInst* basePtr, - IRInst* index) - { - AddressSpace addrSpace = AddressSpace::Generic; - IRInst* valueType = nullptr; - auto basePtrType = unwrapAttributedType(basePtr->getDataType()); - if (auto ptrType = as<IRPtrTypeBase>(basePtrType)) - { - addrSpace = ptrType->getAddressSpace(); - valueType = ptrType->getValueType(); - } - else if (auto ptrLikeType = as<IRPointerLikeType>(basePtrType)) - { - valueType = ptrLikeType->getElementType(); - } - IRType* type = nullptr; - valueType = unwrapAttributedType(valueType); - if (auto arrayType = as<IRArrayTypeBase>(valueType)) - { - type = arrayType->getElementType(); - } - else if (auto vectorType = as<IRVectorType>(valueType)) - { - type = vectorType->getElementType(); - } - else if (auto matrixType = as<IRMatrixType>(valueType)) - { - type = getVectorType(matrixType->getElementType(), matrixType->getColumnCount()); - } - else if (const auto basicType = as<IRBasicType>(valueType)) - { - // HLSL support things like float.x, in which case we just return the base pointer. - return basePtr; - } - else if (const auto tupleType = as<IRTupleType>(valueType)) - { - SLANG_ASSERT(as<IRIntLit>(index)); - type = (IRType*)tupleType->getOperand(getIntVal(index)); - } - SLANG_RELEASE_ASSERT(type); - auto inst = createInst<IRGetElementPtr>( - this, - kIROp_GetElementPtr, - getPtrType(kIROp_PtrType, type, addrSpace), - basePtr, - index); - - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitElementExtract(IRInst* base, IRIntegerValue index) +{ + return emitElementExtract(base, getIntValue(getIntType(), index)); +} - IRInst* IRBuilder::emitElementAddress( - IRInst* basePtr, - const ArrayView<IRInst*>& accessChain) +IRInst* IRBuilder::emitElementExtract(IRInst* base, const ArrayView<IRInst*>& accessChain) +{ + for (auto access : accessChain) { - for (auto access : accessChain) + IRType* resultType = nullptr; + if (auto structKey = as<IRStructKey>(access)) { - if (auto structKey = as<IRStructKey>(access)) - { - basePtr = emitFieldAddress(basePtr, structKey); - } - else + auto structType = as<IRStructType>(base->getDataType()); + SLANG_RELEASE_ASSERT(structType); + for (auto field : structType->getFields()) { - basePtr = emitElementAddress(basePtr, access); + if (field->getKey() == structKey) + { + resultType = field->getFieldType(); + break; + } } + SLANG_RELEASE_ASSERT(resultType); + base = emitFieldExtract(resultType, base, structKey); } - return basePtr; - } - - IRInst* IRBuilder::emitElementAddress( - IRInst* basePtr, - const ArrayView<IRInst*>& accessChain, - const ArrayView<IRInst*>& types) - { - for (Index i = 0; i < accessChain.getCount(); i++) + else { - auto access = accessChain[i]; - auto type = (IRType*)types[i]; - if (auto structKey = as<IRStructKey>(access)) - { - basePtr = emitFieldAddress(type, basePtr, structKey); - } - else - { - basePtr = emitElementAddress(type, basePtr, access); - } + base = emitElementExtract(base, access); } - return basePtr; } + return base; +} - IRInst* IRBuilder::emitUpdateElement(IRInst* base, IRInst* index, IRInst* newElement) - { - auto inst = createInst<IRUpdateElement>( - this, - kIROp_UpdateElement, - base->getFullType(), - base, - newElement, - index); +IRInst* IRBuilder::emitElementAddress(IRType* type, IRInst* basePtr, IRInst* index) +{ + // Propagate pointer address space if it is available on base. + type = maybePropagateAddressSpace(this, basePtr, type); - addInst(inst); - return inst; - } + auto inst = createInst<IRFieldAddress>(this, kIROp_GetElementPtr, type, basePtr, index); - IRInst* IRBuilder::emitUpdateElement(IRInst* base, IRIntegerValue index, IRInst* newElement) - { - return emitUpdateElement(base, getIntValue(getIntType(), index), newElement); - } + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitUpdateElement(IRInst* base, ArrayView<IRInst*> accessChain, IRInst* newElement) +IRInst* IRBuilder::emitElementAddress(IRInst* basePtr, IRIntegerValue index) +{ + return emitElementAddress(basePtr, getIntValue(getIntType(), index)); +} + +IRInst* IRBuilder::emitElementAddress(IRInst* basePtr, IRInst* index) +{ + AddressSpace addrSpace = AddressSpace::Generic; + IRInst* valueType = nullptr; + auto basePtrType = unwrapAttributedType(basePtr->getDataType()); + if (auto ptrType = as<IRPtrTypeBase>(basePtrType)) { - List<IRInst*> args; - args.add(base); - args.add(newElement); - args.addRange(accessChain); - auto inst = createInst<IRUpdateElement>( - this, kIROp_UpdateElement, base->getFullType(), (Int)args.getCount(), args.getBuffer()); - addInst(inst); - return inst; + addrSpace = ptrType->getAddressSpace(); + valueType = ptrType->getValueType(); } - - IRInst* IRBuilder::emitGetOffsetPtr(IRInst* base, IRInst* offset) + else if (auto ptrLikeType = as<IRPointerLikeType>(basePtrType)) { - IRInst* args[] = { base, offset }; - return emitIntrinsicInst(base->getDataType(), kIROp_GetOffsetPtr, 2, args); + valueType = ptrLikeType->getElementType(); } - - IRInst* IRBuilder::emitGetAddress( - IRType* type, - IRInst* value) + IRType* type = nullptr; + valueType = unwrapAttributedType(valueType); + if (auto arrayType = as<IRArrayTypeBase>(valueType)) { - auto inst = createInst<IRGetAddress>( - this, - kIROp_GetAddr, - type, - value); - - addInst(inst); - return inst; + type = arrayType->getElementType(); } - - IRInst* IRBuilder::emitSwizzle( - IRType* type, - IRInst* base, - UInt elementCount, - IRInst* const* elementIndices) + else if (auto vectorType = as<IRVectorType>(valueType)) { - auto inst = createInstWithTrailingArgs<IRSwizzle>( - this, - kIROp_swizzle, - type, - base, - elementCount, - elementIndices); - - addInst(inst); - return inst; + type = vectorType->getElementType(); } - - IRInst* IRBuilder::addFloatingModeOverrideDecoration(IRInst* dest, FloatingPointMode mode) + else if (auto matrixType = as<IRMatrixType>(valueType)) { - return addDecoration( - dest, - kIROp_FloatingPointModeOverrideDecoration, - getIntValue(getIntType(), (IRIntegerValue)mode)); + type = getVectorType(matrixType->getElementType(), matrixType->getColumnCount()); } - - IRInst* IRBuilder::addNumThreadsDecoration(IRInst* inst, IRInst* x, IRInst* y, IRInst* z) + else if (const auto basicType = as<IRBasicType>(valueType)) { - IRInst* operands[3] = { - x, - y, - z - }; - - return addDecoration(inst, kIROp_NumThreadsDecoration, operands, 3); + // HLSL support things like float.x, in which case we just return the base pointer. + return basePtr; } - - IRInst* IRBuilder::addWaveSizeDecoration(IRInst* inst, IRInst* numLanes) + else if (const auto tupleType = as<IRTupleType>(valueType)) { - IRInst* operands[1] = { - numLanes - }; - - return addDecoration(inst, kIROp_WaveSizeDecoration, operands, 1); + SLANG_ASSERT(as<IRIntLit>(index)); + type = (IRType*)tupleType->getOperand(getIntVal(index)); } + SLANG_RELEASE_ASSERT(type); + auto inst = createInst<IRGetElementPtr>( + this, + kIROp_GetElementPtr, + getPtrType(kIROp_PtrType, type, addrSpace), + basePtr, + index); - IRInst* IRBuilder::emitSwizzle( - IRType* type, - IRInst* base, - UInt elementCount, - UInt const* elementIndices) - { - auto intType = getBasicType(BaseType::Int); + addInst(inst); + return inst; +} - IRInst* irElementIndices[4]; - for (UInt ii = 0; ii < elementCount; ++ii) +IRInst* IRBuilder::emitElementAddress(IRInst* basePtr, const ArrayView<IRInst*>& accessChain) +{ + for (auto access : accessChain) + { + if (auto structKey = as<IRStructKey>(access)) { - irElementIndices[ii] = getIntValue(intType, elementIndices[ii]); + basePtr = emitFieldAddress(basePtr, structKey); + } + else + { + basePtr = emitElementAddress(basePtr, access); } - - return emitSwizzle(type, base, elementCount, irElementIndices); - } - - IRMetalSetVertex* IRBuilder::emitMetalSetVertex( - IRInst* index, - IRInst* vertex) - { - auto inst = createInst<IRMetalSetVertex>(this, kIROp_MetalSetVertex, getVoidType(), index, vertex); - addInst(inst); - return inst; } + return basePtr; +} - IRMetalSetPrimitive* IRBuilder::emitMetalSetPrimitive( - IRInst* index, - IRInst* primitive) +IRInst* IRBuilder::emitElementAddress( + IRInst* basePtr, + const ArrayView<IRInst*>& accessChain, + const ArrayView<IRInst*>& types) +{ + for (Index i = 0; i < accessChain.getCount(); i++) { - auto inst = createInst<IRMetalSetPrimitive>(this, kIROp_MetalSetVertex, getVoidType(), index, primitive); - addInst(inst); - return inst; + auto access = accessChain[i]; + auto type = (IRType*)types[i]; + if (auto structKey = as<IRStructKey>(access)) + { + basePtr = emitFieldAddress(type, basePtr, structKey); + } + else + { + basePtr = emitElementAddress(type, basePtr, access); + } } + return basePtr; +} - IRMetalSetIndices* IRBuilder::emitMetalSetIndices( - IRInst* index, - IRInst* indices) - { - auto inst = createInst<IRMetalSetIndices>(this, kIROp_MetalSetVertex, getVoidType(), index, indices); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitUpdateElement(IRInst* base, IRInst* index, IRInst* newElement) +{ + auto inst = createInst<IRUpdateElement>( + this, + kIROp_UpdateElement, + base->getFullType(), + base, + newElement, + index); + + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitUpdateElement(IRInst* base, IRIntegerValue index, IRInst* newElement) +{ + return emitUpdateElement(base, getIntValue(getIntType(), index), newElement); +} - IRInst* IRBuilder::emitSwizzleSet( - IRType* type, - IRInst* base, - IRInst* source, - UInt elementCount, - IRInst* const* elementIndices) - { - IRInst* fixedArgs[] = { base, source }; - UInt fixedArgCount = sizeof(fixedArgs) / sizeof(fixedArgs[0]); +IRInst* IRBuilder::emitUpdateElement( + IRInst* base, + ArrayView<IRInst*> accessChain, + IRInst* newElement) +{ + List<IRInst*> args; + args.add(base); + args.add(newElement); + args.addRange(accessChain); + auto inst = createInst<IRUpdateElement>( + this, + kIROp_UpdateElement, + base->getFullType(), + (Int)args.getCount(), + args.getBuffer()); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitGetOffsetPtr(IRInst* base, IRInst* offset) +{ + IRInst* args[] = {base, offset}; + return emitIntrinsicInst(base->getDataType(), kIROp_GetOffsetPtr, 2, args); +} - auto inst = createInstWithTrailingArgs<IRSwizzleSet>( - this, - kIROp_swizzleSet, - type, - fixedArgCount, - fixedArgs, - elementCount, - elementIndices); +IRInst* IRBuilder::emitGetAddress(IRType* type, IRInst* value) +{ + auto inst = createInst<IRGetAddress>(this, kIROp_GetAddr, type, value); - addInst(inst); - return inst; - } + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitSwizzleSet( - IRType* type, - IRInst* base, - IRInst* source, - UInt elementCount, - UInt const* elementIndices) - { - auto intType = getBasicType(BaseType::Int); +IRInst* IRBuilder::emitSwizzle( + IRType* type, + IRInst* base, + UInt elementCount, + IRInst* const* elementIndices) +{ + auto inst = createInstWithTrailingArgs<IRSwizzle>( + this, + kIROp_swizzle, + type, + base, + elementCount, + elementIndices); + + addInst(inst); + return inst; +} + +IRInst* IRBuilder::addFloatingModeOverrideDecoration(IRInst* dest, FloatingPointMode mode) +{ + return addDecoration( + dest, + kIROp_FloatingPointModeOverrideDecoration, + getIntValue(getIntType(), (IRIntegerValue)mode)); +} - IRInst* irElementIndices[4]; - for (UInt ii = 0; ii < elementCount; ++ii) - { - irElementIndices[ii] = getIntValue(intType, elementIndices[ii]); - } +IRInst* IRBuilder::addNumThreadsDecoration(IRInst* inst, IRInst* x, IRInst* y, IRInst* z) +{ + IRInst* operands[3] = {x, y, z}; - return emitSwizzleSet(type, base, source, elementCount, irElementIndices); - } + return addDecoration(inst, kIROp_NumThreadsDecoration, operands, 3); +} - IRInst* IRBuilder::emitSwizzledStore( - IRInst* dest, - IRInst* source, - UInt elementCount, - IRInst* const* elementIndices) - { - IRInst* fixedArgs[] = { dest, source }; - UInt fixedArgCount = sizeof(fixedArgs) / sizeof(fixedArgs[0]); +IRInst* IRBuilder::addWaveSizeDecoration(IRInst* inst, IRInst* numLanes) +{ + IRInst* operands[1] = {numLanes}; - auto inst = createInstImpl<IRSwizzledStore>( - this, - kIROp_SwizzledStore, - nullptr, - fixedArgCount, - fixedArgs, - elementCount, - elementIndices); + return addDecoration(inst, kIROp_WaveSizeDecoration, operands, 1); +} - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitSwizzle( + IRType* type, + IRInst* base, + UInt elementCount, + UInt const* elementIndices) +{ + auto intType = getBasicType(BaseType::Int); - IRInst* IRBuilder::emitSwizzledStore( - IRInst* dest, - IRInst* source, - UInt elementCount, - UInt const* elementIndices) + IRInst* irElementIndices[4]; + for (UInt ii = 0; ii < elementCount; ++ii) { - auto intType = getBasicType(BaseType::Int); + irElementIndices[ii] = getIntValue(intType, elementIndices[ii]); + } - IRInst* irElementIndices[4]; - for (UInt ii = 0; ii < elementCount; ++ii) - { - irElementIndices[ii] = getIntValue(intType, elementIndices[ii]); - } + return emitSwizzle(type, base, elementCount, irElementIndices); +} - return emitSwizzledStore(dest, source, elementCount, irElementIndices); - } +IRMetalSetVertex* IRBuilder::emitMetalSetVertex(IRInst* index, IRInst* vertex) +{ + auto inst = + createInst<IRMetalSetVertex>(this, kIROp_MetalSetVertex, getVoidType(), index, vertex); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitReturn( - IRInst* val) - { - auto inst = createInst<IRReturn>( - this, - kIROp_Return, - nullptr, - val); - addInst(inst); - return inst; - } +IRMetalSetPrimitive* IRBuilder::emitMetalSetPrimitive(IRInst* index, IRInst* primitive) +{ + auto inst = createInst<IRMetalSetPrimitive>( + this, + kIROp_MetalSetVertex, + getVoidType(), + index, + primitive); + addInst(inst); + return inst; +} + +IRMetalSetIndices* IRBuilder::emitMetalSetIndices(IRInst* index, IRInst* indices) +{ + auto inst = + createInst<IRMetalSetIndices>(this, kIROp_MetalSetVertex, getVoidType(), index, indices); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitSwizzleSet( + IRType* type, + IRInst* base, + IRInst* source, + UInt elementCount, + IRInst* const* elementIndices) +{ + IRInst* fixedArgs[] = {base, source}; + UInt fixedArgCount = sizeof(fixedArgs) / sizeof(fixedArgs[0]); + + auto inst = createInstWithTrailingArgs<IRSwizzleSet>( + this, + kIROp_swizzleSet, + type, + fixedArgCount, + fixedArgs, + elementCount, + elementIndices); + + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitSwizzleSet( + IRType* type, + IRInst* base, + IRInst* source, + UInt elementCount, + UInt const* elementIndices) +{ + auto intType = getBasicType(BaseType::Int); - IRInst* IRBuilder::emitYield( - IRInst* val) + IRInst* irElementIndices[4]; + for (UInt ii = 0; ii < elementCount; ++ii) { - auto inst = createInst<IRYield>( - this, - kIROp_Yield, - nullptr, - val); - addInst(inst); - return inst; + irElementIndices[ii] = getIntValue(intType, elementIndices[ii]); } - IRInst* IRBuilder::emitReturn() - { - auto voidVal = getVoidValue(); - auto inst = createInst<IRReturn>(this, kIROp_Return, nullptr, voidVal); - addInst(inst); - return inst; - } + return emitSwizzleSet(type, base, source, elementCount, irElementIndices); +} - IRInst* IRBuilder::emitThrow(IRInst* val) - { - auto inst = createInst<IRThrow>(this, kIROp_Throw, nullptr, val); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitSwizzledStore( + IRInst* dest, + IRInst* source, + UInt elementCount, + IRInst* const* elementIndices) +{ + IRInst* fixedArgs[] = {dest, source}; + UInt fixedArgCount = sizeof(fixedArgs) / sizeof(fixedArgs[0]); + + auto inst = createInstImpl<IRSwizzledStore>( + this, + kIROp_SwizzledStore, + nullptr, + fixedArgCount, + fixedArgs, + elementCount, + elementIndices); + + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitSwizzledStore( + IRInst* dest, + IRInst* source, + UInt elementCount, + UInt const* elementIndices) +{ + auto intType = getBasicType(BaseType::Int); - IRInst* IRBuilder::emitUnreachable() + IRInst* irElementIndices[4]; + for (UInt ii = 0; ii < elementCount; ++ii) { - auto inst = createInst<IRUnreachable>( - this, - kIROp_Unreachable, - nullptr); - addInst(inst); - return inst; + irElementIndices[ii] = getIntValue(intType, elementIndices[ii]); } - IRInst* IRBuilder::emitMissingReturn() - { - auto inst = createInst<IRMissingReturn>( - this, - kIROp_MissingReturn, - nullptr); - addInst(inst); - return inst; - } + return emitSwizzledStore(dest, source, elementCount, irElementIndices); +} - IRInst* IRBuilder::emitDiscard() - { - auto inst = createInst<IRDiscard>( - this, - kIROp_discard, - nullptr); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitReturn(IRInst* val) +{ + auto inst = createInst<IRReturn>(this, kIROp_Return, nullptr, val); + addInst(inst); + return inst; +} +IRInst* IRBuilder::emitYield(IRInst* val) +{ + auto inst = createInst<IRYield>(this, kIROp_Yield, nullptr, val); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitBranch( - IRBlock* pBlock) - { - auto inst = createInst<IRUnconditionalBranch>( - this, - kIROp_unconditionalBranch, - nullptr, - pBlock); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitReturn() +{ + auto voidVal = getVoidValue(); + auto inst = createInst<IRReturn>(this, kIROp_Return, nullptr, voidVal); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitBranch(IRBlock* block, Int argCount, IRInst* const* args) - { - List<IRInst*> argList; - argList.add(block); - for (Int i = 0; i < argCount; ++i) - argList.add(args[i]); - auto inst = - createInst<IRUnconditionalBranch>(this, kIROp_unconditionalBranch, nullptr, argList.getCount(), argList.getBuffer()); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitThrow(IRInst* val) +{ + auto inst = createInst<IRThrow>(this, kIROp_Throw, nullptr, val); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitBreak( - IRBlock* target) - { - return emitBranch(target); - } +IRInst* IRBuilder::emitUnreachable() +{ + auto inst = createInst<IRUnreachable>(this, kIROp_Unreachable, nullptr); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitContinue( - IRBlock* target) - { - return emitBranch(target); - } +IRInst* IRBuilder::emitMissingReturn() +{ + auto inst = createInst<IRMissingReturn>(this, kIROp_MissingReturn, nullptr); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitLoop( - IRBlock* target, - IRBlock* breakBlock, - IRBlock* continueBlock) - { - IRInst* args[] = { target, breakBlock, continueBlock }; - UInt argCount = sizeof(args) / sizeof(args[0]); +IRInst* IRBuilder::emitDiscard() +{ + auto inst = createInst<IRDiscard>(this, kIROp_discard, nullptr); + addInst(inst); + return inst; +} - auto inst = createInst<IRLoop>( - this, - kIROp_loop, - nullptr, - argCount, - args); - addInst(inst); - return inst; - } - IRInst* IRBuilder::emitLoop( - IRBlock* target, - IRBlock* breakBlock, - IRBlock* continueBlock, - Int argCount, - IRInst*const* args) - { - List<IRInst*> argList; - - argList.add(target); - argList.add(breakBlock); - argList.add(continueBlock); - - for (Count ii = 0; ii < argCount; ii++) - argList.add(args[ii]); - - auto inst = createInst<IRLoop>( - this, - kIROp_loop, - nullptr, - argList.getCount(), - argList.getBuffer()); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitBranch(IRBlock* pBlock) +{ + auto inst = createInst<IRUnconditionalBranch>(this, kIROp_unconditionalBranch, nullptr, pBlock); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitBranch( - IRInst* val, - IRBlock* trueBlock, - IRBlock* falseBlock) - { - IRInst* args[] = { val, trueBlock, falseBlock }; - UInt argCount = sizeof(args) / sizeof(args[0]); +IRInst* IRBuilder::emitBranch(IRBlock* block, Int argCount, IRInst* const* args) +{ + List<IRInst*> argList; + argList.add(block); + for (Int i = 0; i < argCount; ++i) + argList.add(args[i]); + auto inst = createInst<IRUnconditionalBranch>( + this, + kIROp_unconditionalBranch, + nullptr, + argList.getCount(), + argList.getBuffer()); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitBreak(IRBlock* target) +{ + return emitBranch(target); +} - auto inst = createInst<IRConditionalBranch>( - this, - kIROp_conditionalBranch, - nullptr, - argCount, - args); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitContinue(IRBlock* target) +{ + return emitBranch(target); +} - IRIfElse* IRBuilder::emitIfElse( - IRInst* val, - IRBlock* trueBlock, - IRBlock* falseBlock, - IRBlock* afterBlock) - { - IRInst* args[] = { val, trueBlock, falseBlock, afterBlock }; - UInt argCount = sizeof(args) / sizeof(args[0]); +IRInst* IRBuilder::emitLoop(IRBlock* target, IRBlock* breakBlock, IRBlock* continueBlock) +{ + IRInst* args[] = {target, breakBlock, continueBlock}; + UInt argCount = sizeof(args) / sizeof(args[0]); + + auto inst = createInst<IRLoop>(this, kIROp_loop, nullptr, argCount, args); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitLoop( + IRBlock* target, + IRBlock* breakBlock, + IRBlock* continueBlock, + Int argCount, + IRInst* const* args) +{ + List<IRInst*> argList; - auto inst = createInst<IRIfElse>( - this, - kIROp_ifElse, - nullptr, - argCount, - args); - addInst(inst); - return inst; - } + argList.add(target); + argList.add(breakBlock); + argList.add(continueBlock); - IRInst* IRBuilder::emitIfElseWithBlocks( - IRInst* val, IRBlock*& outTrueBlock, IRBlock*& outFalseBlock, IRBlock*& outAfterBlock) - { - outTrueBlock = createBlock(); - outAfterBlock = createBlock(); - outFalseBlock = createBlock(); - auto f = getFunc(); - SLANG_ASSERT(f); - if (f) - { - f->addBlock(outTrueBlock); - f->addBlock(outAfterBlock); - f->addBlock(outFalseBlock); - } - auto result = emitIfElse(val, outTrueBlock, outFalseBlock, outAfterBlock); - setInsertInto(outTrueBlock); - return result; - } + for (Count ii = 0; ii < argCount; ii++) + argList.add(args[ii]); - IRInst* IRBuilder::emitIf( - IRInst* val, - IRBlock* trueBlock, - IRBlock* afterBlock) - { - return emitIfElse(val, trueBlock, afterBlock, afterBlock); - } + auto inst = + createInst<IRLoop>(this, kIROp_loop, nullptr, argList.getCount(), argList.getBuffer()); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitIfWithBlocks( - IRInst* val, IRBlock*& outTrueBlock, IRBlock*& outAfterBlock) - { - outTrueBlock = createBlock(); - outAfterBlock = createBlock(); - auto result = emitIf(val, outTrueBlock, outAfterBlock); - insertBlock(outTrueBlock); - insertBlock(outAfterBlock); - setInsertInto(outTrueBlock); - return result; - } +IRInst* IRBuilder::emitBranch(IRInst* val, IRBlock* trueBlock, IRBlock* falseBlock) +{ + IRInst* args[] = {val, trueBlock, falseBlock}; + UInt argCount = sizeof(args) / sizeof(args[0]); + + auto inst = + createInst<IRConditionalBranch>(this, kIROp_conditionalBranch, nullptr, argCount, args); + addInst(inst); + return inst; +} + +IRIfElse* IRBuilder::emitIfElse( + IRInst* val, + IRBlock* trueBlock, + IRBlock* falseBlock, + IRBlock* afterBlock) +{ + IRInst* args[] = {val, trueBlock, falseBlock, afterBlock}; + UInt argCount = sizeof(args) / sizeof(args[0]); + + auto inst = createInst<IRIfElse>(this, kIROp_ifElse, nullptr, argCount, args); + addInst(inst); + return inst; +} + +IRInst* IRBuilder::emitIfElseWithBlocks( + IRInst* val, + IRBlock*& outTrueBlock, + IRBlock*& outFalseBlock, + IRBlock*& outAfterBlock) +{ + outTrueBlock = createBlock(); + outAfterBlock = createBlock(); + outFalseBlock = createBlock(); + auto f = getFunc(); + SLANG_ASSERT(f); + if (f) + { + f->addBlock(outTrueBlock); + f->addBlock(outAfterBlock); + f->addBlock(outFalseBlock); + } + auto result = emitIfElse(val, outTrueBlock, outFalseBlock, outAfterBlock); + setInsertInto(outTrueBlock); + return result; +} + +IRInst* IRBuilder::emitIf(IRInst* val, IRBlock* trueBlock, IRBlock* afterBlock) +{ + return emitIfElse(val, trueBlock, afterBlock, afterBlock); +} - IRInst* IRBuilder::emitLoopTest( - IRInst* val, - IRBlock* bodyBlock, - IRBlock* breakBlock) - { - return emitIfElse(val, bodyBlock, breakBlock, bodyBlock); - } +IRInst* IRBuilder::emitIfWithBlocks(IRInst* val, IRBlock*& outTrueBlock, IRBlock*& outAfterBlock) +{ + outTrueBlock = createBlock(); + outAfterBlock = createBlock(); + auto result = emitIf(val, outTrueBlock, outAfterBlock); + insertBlock(outTrueBlock); + insertBlock(outAfterBlock); + setInsertInto(outTrueBlock); + return result; +} + +IRInst* IRBuilder::emitLoopTest(IRInst* val, IRBlock* bodyBlock, IRBlock* breakBlock) +{ + return emitIfElse(val, bodyBlock, breakBlock, bodyBlock); +} + +IRInst* IRBuilder::emitSwitch( + IRInst* val, + IRBlock* breakLabel, + IRBlock* defaultLabel, + UInt caseArgCount, + IRInst* const* caseArgs) +{ + IRInst* fixedArgs[] = {val, breakLabel, defaultLabel}; + UInt fixedArgCount = sizeof(fixedArgs) / sizeof(fixedArgs[0]); + + auto inst = createInstWithTrailingArgs<IRSwitch>( + this, + kIROp_Switch, + nullptr, + fixedArgCount, + fixedArgs, + caseArgCount, + caseArgs); + addInst(inst); + return inst; +} + +IRGlobalGenericParam* IRBuilder::emitGlobalGenericParam(IRType* type) +{ + IRGlobalGenericParam* irGenericParam = + createInst<IRGlobalGenericParam>(this, kIROp_GlobalGenericParam, type); + addGlobalValue(this, irGenericParam); + return irGenericParam; +} - IRInst* IRBuilder::emitSwitch( - IRInst* val, - IRBlock* breakLabel, - IRBlock* defaultLabel, - UInt caseArgCount, - IRInst* const* caseArgs) - { - IRInst* fixedArgs[] = { val, breakLabel, defaultLabel }; - UInt fixedArgCount = sizeof(fixedArgs) / sizeof(fixedArgs[0]); +IRBindGlobalGenericParam* IRBuilder::emitBindGlobalGenericParam(IRInst* param, IRInst* val) +{ + auto inst = createInst<IRBindGlobalGenericParam>( + this, + kIROp_BindGlobalGenericParam, + nullptr, + param, + val); + addInst(inst); + return inst; +} + +IRDecoration* IRBuilder::addBindExistentialSlotsDecoration( + IRInst* value, + UInt argCount, + IRInst* const* args) +{ + auto decoration = createInstWithTrailingArgs<IRDecoration>( + this, + kIROp_BindExistentialSlotsDecoration, + getVoidType(), + 0, + nullptr, + argCount, + args); - auto inst = createInstWithTrailingArgs<IRSwitch>( - this, - kIROp_Switch, - nullptr, - fixedArgCount, - fixedArgs, - caseArgCount, - caseArgs); - addInst(inst); - return inst; - } + decoration->insertAtStart(value); - IRGlobalGenericParam* IRBuilder::emitGlobalGenericParam( - IRType* type) - { - IRGlobalGenericParam* irGenericParam = createInst<IRGlobalGenericParam>( - this, - kIROp_GlobalGenericParam, - type); - addGlobalValue(this, irGenericParam); - return irGenericParam; - } + return decoration; +} - IRBindGlobalGenericParam* IRBuilder::emitBindGlobalGenericParam( - IRInst* param, - IRInst* val) - { - auto inst = createInst<IRBindGlobalGenericParam>( - this, - kIROp_BindGlobalGenericParam, - nullptr, - param, - val); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitExtractTaggedUnionTag(IRInst* val) +{ + auto inst = + createInst<IRInst>(this, kIROp_ExtractTaggedUnionTag, getBasicType(BaseType::UInt), val); + addInst(inst); + return inst; +} - IRDecoration* IRBuilder::addBindExistentialSlotsDecoration( - IRInst* value, - UInt argCount, - IRInst* const* args) - { - auto decoration = createInstWithTrailingArgs<IRDecoration>( - this, - kIROp_BindExistentialSlotsDecoration, - getVoidType(), - 0, - nullptr, - argCount, - args); +IRInst* IRBuilder::emitExtractTaggedUnionPayload(IRType* type, IRInst* val, IRInst* tag) +{ + auto inst = createInst<IRInst>(this, kIROp_ExtractTaggedUnionPayload, type, val, tag); + addInst(inst); + return inst; +} - decoration->insertAtStart(value); - return decoration; - } +IRInst* IRBuilder::emitSizeOf(IRInst* sizedType) +{ + auto inst = createInst<IRInst>(this, kIROp_SizeOf, getIntType(), sizedType); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitExtractTaggedUnionTag( - IRInst* val) - { - auto inst = createInst<IRInst>( - this, - kIROp_ExtractTaggedUnionTag, - getBasicType(BaseType::UInt), - val); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitAlignOf(IRInst* sizedType) +{ + auto inst = createInst<IRInst>(this, kIROp_AlignOf, getIntType(), sizedType); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitExtractTaggedUnionPayload( - IRType* type, - IRInst* val, - IRInst* tag) - { - auto inst = createInst<IRInst>( - this, - kIROp_ExtractTaggedUnionPayload, - type, - val, - tag); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitCountOf(IRType* type, IRInst* sizedType) +{ + auto inst = createInst<IRInst>(this, kIROp_CountOf, type, sizedType); + addInst(inst); + return inst; +} +IRInst* IRBuilder::emitBitCast(IRType* type, IRInst* val) +{ + auto inst = createInst<IRInst>(this, kIROp_BitCast, type, val); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitSizeOf( - IRInst* sizedType) - { - auto inst = createInst<IRInst>( - this, - kIROp_SizeOf, - getIntType(), - sizedType); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitCastPtrToBool(IRInst* val) +{ + auto inst = createInst<IRInst>(this, kIROp_CastPtrToBool, getBoolType(), val); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitAlignOf( - IRInst* sizedType) - { - auto inst = createInst<IRInst>( - this, - kIROp_AlignOf, - getIntType(), - sizedType); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitCastPtrToInt(IRInst* val) +{ + auto inst = createInst<IRInst>(this, kIROp_CastPtrToInt, getUInt64Type(), val); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitCountOf( - IRType* type, - IRInst* sizedType) - { - auto inst = createInst<IRInst>( - this, - kIROp_CountOf, - type, - sizedType); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitCastIntToPtr(IRType* ptrType, IRInst* val) +{ + auto inst = createInst<IRInst>(this, kIROp_CastIntToPtr, ptrType, val); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitBitCast( - IRType* type, - IRInst* val) - { - auto inst = createInst<IRInst>( - this, - kIROp_BitCast, - type, - val); - addInst(inst); - return inst; - } +IRGlobalConstant* IRBuilder::emitGlobalConstant(IRType* type) +{ + auto inst = createInst<IRGlobalConstant>(this, kIROp_GlobalConstant, type); + addGlobalValue(this, inst); + return inst; +} - IRInst* IRBuilder::emitCastPtrToBool(IRInst* val) - { - auto inst = createInst<IRInst>( - this, - kIROp_CastPtrToBool, - getBoolType(), - val); - addInst(inst); - return inst; - } +IRGlobalConstant* IRBuilder::emitGlobalConstant(IRType* type, IRInst* val) +{ + auto inst = createInst<IRGlobalConstant>(this, kIROp_GlobalConstant, type, val); + addGlobalValue(this, inst); + return inst; +} - IRInst* IRBuilder::emitCastPtrToInt(IRInst* val) - { - auto inst = createInst<IRInst>( - this, - kIROp_CastPtrToInt, - getUInt64Type(), - val); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitWaveMaskBallot(IRType* type, IRInst* mask, IRInst* condition) +{ + auto inst = createInst<IRInst>(this, kIROp_WaveMaskBallot, type, mask, condition); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitCastIntToPtr(IRType* ptrType, IRInst* val) - { - auto inst = createInst<IRInst>( - this, - kIROp_CastIntToPtr, - ptrType, - val); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitWaveMaskMatch(IRType* type, IRInst* mask, IRInst* value) +{ + auto inst = createInst<IRInst>(this, kIROp_WaveMaskMatch, type, mask, value); + addInst(inst); + return inst; +} - IRGlobalConstant* IRBuilder::emitGlobalConstant( - IRType* type) - { - auto inst = createInst<IRGlobalConstant>( - this, - kIROp_GlobalConstant, - type); - addGlobalValue(this, inst); - return inst; - } +IRInst* IRBuilder::emitBitAnd(IRType* type, IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_BitAnd, type, left, right); + addInst(inst); + return inst; +} - IRGlobalConstant* IRBuilder::emitGlobalConstant( - IRType* type, - IRInst* val) - { - auto inst = createInst<IRGlobalConstant>( - this, - kIROp_GlobalConstant, - type, - val); - addGlobalValue(this, inst); - return inst; - } +IRInst* IRBuilder::emitBitOr(IRType* type, IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_BitOr, type, left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitWaveMaskBallot(IRType* type, IRInst* mask, IRInst* condition) - { - auto inst = createInst<IRInst>( - this, - kIROp_WaveMaskBallot, - type, - mask, - condition); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitBitNot(IRType* type, IRInst* value) +{ + auto inst = createInst<IRInst>(this, kIROp_BitNot, type, value); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitWaveMaskMatch(IRType* type, IRInst* mask, IRInst* value) - { - auto inst = createInst<IRInst>( - this, - kIROp_WaveMaskMatch, - type, - mask, - value); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitNeg(IRType* type, IRInst* value) +{ + auto inst = createInst<IRInst>(this, kIROp_Neg, type, value); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitBitAnd(IRType* type, IRInst* left, IRInst* right) - { - auto inst = createInst<IRInst>( - this, - kIROp_BitAnd, - type, - left, - right); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitNot(IRType* type, IRInst* value) +{ + auto inst = createInst<IRInst>(this, kIROp_Not, type, value); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitBitOr(IRType* type, IRInst* left, IRInst* right) - { - auto inst = createInst<IRInst>( - this, - kIROp_BitOr, - type, - left, - right); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitAdd(IRType* type, IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_Add, type, left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitBitNot(IRType* type, IRInst* value) - { - auto inst = createInst<IRInst>( - this, - kIROp_BitNot, - type, - value); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitSub(IRType* type, IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_Sub, type, left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitNeg(IRType* type, IRInst* value) - { - auto inst = createInst<IRInst>( - this, - kIROp_Neg, - type, - value); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitEql(IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_Eql, getBoolType(), left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitNot(IRType* type, IRInst* value) - { - auto inst = createInst<IRInst>( - this, - kIROp_Not, - type, - value); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitNeq(IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_Neq, getBoolType(), left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitAdd(IRType* type, IRInst* left, IRInst* right) - { - auto inst = createInst<IRInst>( - this, - kIROp_Add, - type, - left, - right); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitLess(IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_Less, getBoolType(), left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitSub(IRType* type, IRInst* left, IRInst* right) - { - auto inst = createInst<IRInst>( - this, - kIROp_Sub, - type, - left, - right); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitGeq(IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_Geq, getBoolType(), left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitEql(IRInst* left, IRInst* right) - { - auto inst = createInst<IRInst>(this, kIROp_Eql, getBoolType(), left, right); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitMul(IRType* type, IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_Mul, type, left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitNeq(IRInst* left, IRInst* right) - { - auto inst = createInst<IRInst>(this, kIROp_Neq, getBoolType(), left, right); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitDiv(IRType* type, IRInst* numerator, IRInst* denominator) +{ + auto inst = createInst<IRInst>(this, kIROp_Div, type, numerator, denominator); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitLess(IRInst* left, IRInst* right) - { - auto inst = createInst<IRInst>(this, kIROp_Less, getBoolType(), left, right); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitShr(IRType* type, IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_Rsh, type, left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitGeq(IRInst* left, IRInst* right) - { - auto inst = createInst<IRInst>(this, kIROp_Geq, getBoolType(), left, right); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitShl(IRType* type, IRInst* left, IRInst* right) +{ + auto inst = createInst<IRInst>(this, kIROp_Lsh, type, left, right); + addInst(inst); + return inst; +} - IRInst* IRBuilder::emitMul(IRType* type, IRInst* left, IRInst* right) +IRInst* IRBuilder::emitGetNativePtr(IRInst* value) +{ + auto valueType = value->getDataType(); + SLANG_RELEASE_ASSERT(valueType); + switch (valueType->getOp()) { - auto inst = createInst<IRInst>( - this, - kIROp_Mul, - type, - left, - right); - addInst(inst); - return inst; + case kIROp_InterfaceType: + return emitIntrinsicInst( + getNativePtrType((IRType*)valueType), + kIROp_GetNativePtr, + 1, + &value); + break; + case kIROp_ComPtrType: + return emitIntrinsicInst( + getNativePtrType((IRType*)valueType->getOperand(0)), + kIROp_GetNativePtr, + 1, + &value); + break; + case kIROp_ExtractExistentialType: return emitGetNativePtr(value->getOperand(0)); + default: + SLANG_UNEXPECTED("invalid operand type for `getNativePtr`."); + UNREACHABLE_RETURN(nullptr); } +} - IRInst* IRBuilder::emitDiv(IRType* type, IRInst* numerator, IRInst* denominator) - { - auto inst = createInst<IRInst>( - this, - kIROp_Div, - type, - numerator, - denominator); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitManagedPtrAttach(IRInst* managedPtrVar, IRInst* value) +{ + IRInst* args[] = {managedPtrVar, value}; + return emitIntrinsicInst(getVoidType(), kIROp_ManagedPtrAttach, 2, args); +} - IRInst* IRBuilder::emitShr(IRType* type, IRInst* left, IRInst* right) - { - auto inst = createInst<IRInst>(this, kIROp_Rsh, type, left, right); - addInst(inst); - return inst; - } +IRInst* IRBuilder::emitManagedPtrDetach(IRType* type, IRInst* managedPtrVal) +{ + return emitIntrinsicInst(type, kIROp_ManagedPtrDetach, 1, &managedPtrVal); +} - IRInst* IRBuilder::emitShl(IRType* type, IRInst* left, IRInst* right) +IRInst* IRBuilder::emitGetManagedPtrWriteRef(IRInst* ptrToManagedPtr) +{ + auto type = ptrToManagedPtr->getDataType(); + auto ptrType = as<IRPtrTypeBase>(type); + SLANG_RELEASE_ASSERT(ptrType); + auto managedPtrType = ptrType->getValueType(); + switch (managedPtrType->getOp()) { - auto inst = createInst<IRInst>(this, kIROp_Lsh, type, left, right); - addInst(inst); - return inst; + case kIROp_InterfaceType: + return emitIntrinsicInst( + getPtrType(getNativePtrType((IRType*)managedPtrType)), + kIROp_GetManagedPtrWriteRef, + 1, + &ptrToManagedPtr); + break; + case kIROp_ComPtrType: + return emitIntrinsicInst( + getPtrType(getNativePtrType((IRType*)managedPtrType->getOperand(0))), + kIROp_GetManagedPtrWriteRef, + 1, + &ptrToManagedPtr); + break; + default: + SLANG_UNEXPECTED("invalid operand type for `getNativePtr`."); + UNREACHABLE_RETURN(nullptr); } +} - IRInst* IRBuilder::emitGetNativePtr(IRInst* value) - { - auto valueType = value->getDataType(); - SLANG_RELEASE_ASSERT(valueType); - switch (valueType->getOp()) - { - case kIROp_InterfaceType: - return emitIntrinsicInst( - getNativePtrType((IRType*)valueType), kIROp_GetNativePtr, 1, &value); - break; - case kIROp_ComPtrType: - return emitIntrinsicInst( - getNativePtrType((IRType*)valueType->getOperand(0)), kIROp_GetNativePtr, 1, &value); - break; - case kIROp_ExtractExistentialType: - return emitGetNativePtr(value->getOperand(0)); - default: - SLANG_UNEXPECTED("invalid operand type for `getNativePtr`."); - UNREACHABLE_RETURN(nullptr); - } - } +IRInst* IRBuilder::emitGpuForeach(List<IRInst*> args) +{ + auto inst = createInst<IRInst>( + this, + kIROp_GpuForeach, + getVoidType(), + args.getCount(), + args.getBuffer()); + addInst(inst); + return inst; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandLiteral(IRInst* literal) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = createInst<IRSPIRVAsmOperand>( + this, + kIROp_SPIRVAsmOperandLiteral, + literal->getFullType(), + literal); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandInst(IRInst* inst) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = + createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandInst, inst->getFullType(), inst); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::createSPIRVAsmOperandInst(IRInst* inst) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + auto i = + createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandInst, inst->getFullType(), inst); + return i; +} +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandConvertTexel(IRInst* inst) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + auto i = createInst<IRSPIRVAsmOperand>( + this, + kIROp_SPIRVAsmOperandConvertTexel, + inst->getFullType(), + inst); + addInst(i); + return i; +} +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandRayPayloadFromLocation(IRInst* inst) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + auto i = createInst<IRSPIRVAsmOperand>( + this, + kIROp_SPIRVAsmOperandRayPayloadFromLocation, + inst->getFullType(), + inst); + addInst(i); + return i; +} +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandRayAttributeFromLocation(IRInst* inst) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + auto i = createInst<IRSPIRVAsmOperand>( + this, + kIROp_SPIRVAsmOperandRayAttributeFromLocation, + inst->getFullType(), + inst); + addInst(i); + return i; +} +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandRayCallableFromLocation(IRInst* inst) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + auto i = createInst<IRSPIRVAsmOperand>( + this, + kIROp_SPIRVAsmOperandRayCallableFromLocation, + inst->getFullType(), + inst); + addInst(i); + return i; +} +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandId(IRInst* inst) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = + createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandId, inst->getFullType(), inst); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandResult() +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandResult, getVoidType()); + addInst(i); + return i; +} - IRInst* IRBuilder::emitManagedPtrAttach(IRInst* managedPtrVar, IRInst* value) - { - IRInst* args[] = { managedPtrVar, value }; - return emitIntrinsicInst(getVoidType(), kIROp_ManagedPtrAttach, 2, args); - } +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandEnum(IRInst* inst) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = + createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandEnum, inst->getFullType(), inst); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandEnum(IRInst* inst, IRType* constantType) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = createInst<IRSPIRVAsmOperand>( + this, + kIROp_SPIRVAsmOperandEnum, + inst->getFullType(), + inst, + constantType); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandBuiltinVar(IRInst* type, IRInst* builtinKind) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = createInst<IRSPIRVAsmOperand>( + this, + kIROp_SPIRVAsmOperandBuiltinVar, + (IRType*)type, + builtinKind); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandGLSL450Set() +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = + createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandGLSL450Set, getVoidType()); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandDebugPrintfSet() +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = + createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandDebugPrintfSet, getVoidType()); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandSampledType(IRType* elementType) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = createInst<IRSPIRVAsmOperand>( + this, + kIROp_SPIRVAsmOperandSampledType, + getTypeType(), + elementType); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandImageType(IRInst* element) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = + createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandImageType, getTypeType(), element); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandSampledImageType(IRInst* element) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = createInst<IRSPIRVAsmOperand>( + this, + kIROp_SPIRVAsmOperandSampledImageType, + getTypeType(), + element); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandTruncate() +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = + createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandTruncate, getVoidType()); + addInst(i); + return i; +} + +IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandEntryPoint() +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + const auto i = + createInst<IRSPIRVAsmOperand>(this, kIROp_SPIRVAsmOperandEntryPoint, getVoidType()); + addInst(i); + return i; +} + +IRSPIRVAsmInst* IRBuilder::emitSPIRVAsmInst(IRInst* opcode, List<IRInst*> operands) +{ + SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); + operands.insert(0, opcode); + const auto i = createInst<IRSPIRVAsmInst>( + this, + kIROp_SPIRVAsmInst, + getVoidType(), + operands.getCount(), + operands.getBuffer()); + addInst(i); + return i; +} + +IRSPIRVAsm* IRBuilder::emitSPIRVAsm(IRType* type) +{ + const auto asmInst = createInst<IRSPIRVAsm>(this, kIROp_SPIRVAsm, type); + addInst(asmInst); + return asmInst; +} - IRInst* IRBuilder::emitManagedPtrDetach(IRType* type, IRInst* managedPtrVal) - { - return emitIntrinsicInst(type, kIROp_ManagedPtrDetach, 1, &managedPtrVal); - } +IRInst* IRBuilder::emitGenericAsm(UnownedStringSlice asmText) +{ + IRInst* arg = getStringValue(asmText); + return emitIntrinsicInst(nullptr, kIROp_GenericAsm, 1, &arg); +} - IRInst* IRBuilder::emitGetManagedPtrWriteRef(IRInst* ptrToManagedPtr) +IRInst* IRBuilder::emitRWStructuredBufferGetElementPtr(IRInst* structuredBuffer, IRInst* index) +{ + const auto sbt = cast<IRHLSLRWStructuredBufferType>(structuredBuffer->getDataType()); + const auto t = getPtrType(sbt->getElementType()); + IRInst* const operands[2] = {structuredBuffer, index}; + const auto i = createInst<IRRWStructuredBufferGetElementPtr>( + this, + kIROp_RWStructuredBufferGetElementPtr, + t, + 2, + operands); + addInst(i); + return i; +} + +// IR emitter for a dedicated instruction to represent NonUniformResourceIndex qualifier. +IRInst* IRBuilder::emitNonUniformResourceIndexInst(IRInst* val) +{ + const auto i = createInst<IRInst>(this, kIROp_NonUniformResourceIndex, getTypeType(), val); + addInst(i); + return i; +} + +// +// Decorations +// + +IRDecoration* IRBuilder::addDecoration( + IRInst* value, + IROp op, + IRInst* const* operands, + Int operandCount) +{ + // If it's a simple (ie stateless) decoration, don't add it again. + if (operandCount == 0 && isSimpleDecoration(op)) { - auto type = ptrToManagedPtr->getDataType(); - auto ptrType = as<IRPtrTypeBase>(type); - SLANG_RELEASE_ASSERT(ptrType); - auto managedPtrType = ptrType->getValueType(); - switch (managedPtrType->getOp()) + auto decoration = value->findDecorationImpl(op); + if (decoration) { - case kIROp_InterfaceType: - return emitIntrinsicInst( - getPtrType(getNativePtrType((IRType*)managedPtrType)), kIROp_GetManagedPtrWriteRef, 1, &ptrToManagedPtr); - break; - case kIROp_ComPtrType: - return emitIntrinsicInst( - getPtrType(getNativePtrType((IRType*)managedPtrType->getOperand(0))), kIROp_GetManagedPtrWriteRef, 1, &ptrToManagedPtr); - break; - default: - SLANG_UNEXPECTED("invalid operand type for `getNativePtr`."); - UNREACHABLE_RETURN(nullptr); + return decoration; } } - IRInst* IRBuilder::emitGpuForeach(List<IRInst*> args) - { - auto inst = createInst<IRInst>( - this, - kIROp_GpuForeach, - getVoidType(), - args.getCount(), - args.getBuffer()); - addInst(inst); - return inst; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandLiteral(IRInst* literal) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandLiteral, - literal->getFullType(), - literal - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandInst(IRInst* inst) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandInst, - inst->getFullType(), - inst - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::createSPIRVAsmOperandInst(IRInst* inst) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandInst, - inst->getFullType(), - inst - ); - return i; - } - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandConvertTexel(IRInst* inst) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandConvertTexel, - inst->getFullType(), - inst - ); - addInst(i); - return i; - } - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandRayPayloadFromLocation(IRInst* inst) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandRayPayloadFromLocation, - inst->getFullType(), - inst - ); - addInst(i); - return i; - } - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandRayAttributeFromLocation(IRInst* inst) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandRayAttributeFromLocation, - inst->getFullType(), - inst - ); - addInst(i); - return i; - } - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandRayCallableFromLocation(IRInst* inst) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandRayCallableFromLocation, - inst->getFullType(), - inst - ); - addInst(i); - return i; - } - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandId(IRInst* inst) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandId, - inst->getFullType(), - inst - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandResult() - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandResult, - getVoidType() - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandEnum(IRInst* inst) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandEnum, - inst->getFullType(), - inst - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandEnum(IRInst* inst, IRType* constantType) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandEnum, - inst->getFullType(), - inst, - constantType - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandBuiltinVar(IRInst* type, IRInst* builtinKind) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandBuiltinVar, - (IRType*)type, - builtinKind - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandGLSL450Set() - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandGLSL450Set, - getVoidType() - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandDebugPrintfSet() - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandDebugPrintfSet, - getVoidType() - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandSampledType(IRType* elementType) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandSampledType, - getTypeType(), - elementType - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandImageType(IRInst* element) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandImageType, - getTypeType(), - element - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandSampledImageType(IRInst* element) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandSampledImageType, - getTypeType(), - element - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandTruncate() - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandTruncate, - getVoidType() - ); - addInst(i); - return i; - } - - IRSPIRVAsmOperand* IRBuilder::emitSPIRVAsmOperandEntryPoint() - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - const auto i = createInst<IRSPIRVAsmOperand>( - this, - kIROp_SPIRVAsmOperandEntryPoint, - getVoidType() - ); - addInst(i); - return i; - } + auto decoration = + createInstWithTrailingArgs<IRDecoration>(this, op, getVoidType(), operandCount, operands); - IRSPIRVAsmInst* IRBuilder::emitSPIRVAsmInst(IRInst* opcode, List<IRInst*> operands) - { - SLANG_ASSERT(as<IRSPIRVAsm>(m_insertLoc.getParent())); - operands.insert(0, opcode); - const auto i = createInst<IRSPIRVAsmInst>( - this, - kIROp_SPIRVAsmInst, - getVoidType(), - operands.getCount(), - operands.getBuffer() - ); - addInst(i); - return i; - } + // Decoration order should not, in general, be semantically + // meaningful, so we will elect to insert a new decoration + // at the start of an instruction (constant time) rather + // than at the end of any existing list of deocrations + // (which would take time linear in the number of decorations). + // + // TODO: revisit this if maintaining decoration ordering + // from input source code is desirable. + // + decoration->insertAtStart(value); - IRSPIRVAsm* IRBuilder::emitSPIRVAsm(IRType* type) - { - const auto asmInst = createInst<IRSPIRVAsm>( - this, - kIROp_SPIRVAsm, - type - ); - addInst(asmInst); - return asmInst; - } + return decoration; +} - IRInst* IRBuilder::emitGenericAsm(UnownedStringSlice asmText) - { - IRInst* arg = getStringValue(asmText); - return emitIntrinsicInst(nullptr, kIROp_GenericAsm, 1, &arg); - } - IRInst* IRBuilder::emitRWStructuredBufferGetElementPtr(IRInst* structuredBuffer, IRInst* index) - { - const auto sbt = cast<IRHLSLRWStructuredBufferType>(structuredBuffer->getDataType()); - const auto t = getPtrType(sbt->getElementType()); - IRInst* const operands[2] = {structuredBuffer, index}; - const auto i = createInst<IRRWStructuredBufferGetElementPtr>( - this, - kIROp_RWStructuredBufferGetElementPtr, - t, - 2, - operands - ); - addInst(i); - return i; - } +void IRBuilder::addHighLevelDeclDecoration(IRInst* inst, Decl* decl) +{ + auto ptrConst = _getPtrValue(decl); + addDecoration(inst, kIROp_HighLevelDeclDecoration, ptrConst); +} - // IR emitter for a dedicated instruction to represent NonUniformResourceIndex qualifier. - IRInst* IRBuilder::emitNonUniformResourceIndexInst(IRInst* val) - { - const auto i = createInst<IRInst>( - this, - kIROp_NonUniformResourceIndex, - getTypeType(), - val); - addInst(i); - return i; - } +IRLayoutDecoration* IRBuilder::addLayoutDecoration(IRInst* value, IRLayout* layout) +{ + return as<IRLayoutDecoration>(addDecoration(value, kIROp_LayoutDecoration, layout)); +} - // - // Decorations - // +IRTypeSizeAttr* IRBuilder::getTypeSizeAttr(LayoutResourceKind kind, LayoutSize size) +{ + auto kindInst = getIntValue(getIntType(), IRIntegerValue(kind)); + auto sizeInst = getIntValue(getIntType(), IRIntegerValue(size.raw)); - IRDecoration* IRBuilder::addDecoration(IRInst* value, IROp op, IRInst* const* operands, Int operandCount) - { - // If it's a simple (ie stateless) decoration, don't add it again. - if (operandCount == 0 && isSimpleDecoration(op)) - { - auto decoration = value->findDecorationImpl(op); - if (decoration) - { - return decoration; - } - } + IRInst* operands[] = {kindInst, sizeInst}; - auto decoration = createInstWithTrailingArgs<IRDecoration>( - this, - op, - getVoidType(), - operandCount, - operands); - - // Decoration order should not, in general, be semantically - // meaningful, so we will elect to insert a new decoration - // at the start of an instruction (constant time) rather - // than at the end of any existing list of deocrations - // (which would take time linear in the number of decorations). - // - // TODO: revisit this if maintaining decoration ordering - // from input source code is desirable. - // - decoration->insertAtStart(value); + return cast<IRTypeSizeAttr>( + createIntrinsicInst(getVoidType(), kIROp_TypeSizeAttr, SLANG_COUNT_OF(operands), operands)); +} - return decoration; - } +IRVarOffsetAttr* IRBuilder::getVarOffsetAttr(LayoutResourceKind kind, UInt offset, UInt space) +{ + IRInst* operands[3]; + UInt operandCount = 0; + auto kindInst = getIntValue(getIntType(), IRIntegerValue(kind)); + operands[operandCount++] = kindInst; - void IRBuilder::addHighLevelDeclDecoration(IRInst* inst, Decl* decl) - { - auto ptrConst = _getPtrValue(decl); - addDecoration(inst, kIROp_HighLevelDeclDecoration, ptrConst); - } + auto offsetInst = getIntValue(getIntType(), IRIntegerValue(offset)); + operands[operandCount++] = offsetInst; - IRLayoutDecoration* IRBuilder::addLayoutDecoration(IRInst* value, IRLayout* layout) + if (space) { - return as<IRLayoutDecoration>(addDecoration(value, kIROp_LayoutDecoration, layout)); + auto spaceInst = getIntValue(getIntType(), IRIntegerValue(space)); + operands[operandCount++] = spaceInst; } - IRTypeSizeAttr* IRBuilder::getTypeSizeAttr( - LayoutResourceKind kind, - LayoutSize size) - { - auto kindInst = getIntValue(getIntType(), IRIntegerValue(kind)); - auto sizeInst = getIntValue(getIntType(), IRIntegerValue(size.raw)); - - IRInst* operands[] = { kindInst, sizeInst }; + return cast<IRVarOffsetAttr>( + createIntrinsicInst(getVoidType(), kIROp_VarOffsetAttr, operandCount, operands)); +} - return cast<IRTypeSizeAttr>(createIntrinsicInst( - getVoidType(), - kIROp_TypeSizeAttr, - SLANG_COUNT_OF(operands), - operands)); - } +IRPendingLayoutAttr* IRBuilder::getPendingLayoutAttr(IRLayout* pendingLayout) +{ + IRInst* operands[] = {pendingLayout}; - IRVarOffsetAttr* IRBuilder::getVarOffsetAttr( - LayoutResourceKind kind, - UInt offset, - UInt space) - { - IRInst* operands[3]; - UInt operandCount = 0; + return cast<IRPendingLayoutAttr>(createIntrinsicInst( + getVoidType(), + kIROp_PendingLayoutAttr, + SLANG_COUNT_OF(operands), + operands)); +} - auto kindInst = getIntValue(getIntType(), IRIntegerValue(kind)); - operands[operandCount++] = kindInst; +IRStructFieldLayoutAttr* IRBuilder::getFieldLayoutAttr(IRInst* key, IRVarLayout* layout) +{ + IRInst* operands[] = {key, layout}; - auto offsetInst = getIntValue(getIntType(), IRIntegerValue(offset)); - operands[operandCount++] = offsetInst; + return cast<IRStructFieldLayoutAttr>(createIntrinsicInst( + getVoidType(), + kIROp_StructFieldLayoutAttr, + SLANG_COUNT_OF(operands), + operands)); +} - if(space) - { - auto spaceInst = getIntValue(getIntType(), IRIntegerValue(space)); - operands[operandCount++] = spaceInst; - } +IRTupleFieldLayoutAttr* IRBuilder::getTupleFieldLayoutAttr(IRTypeLayout* layout) +{ + IRInst* operands[] = {layout}; - return cast<IRVarOffsetAttr>(createIntrinsicInst( - getVoidType(), - kIROp_VarOffsetAttr, - operandCount, - operands)); - } + return cast<IRTupleFieldLayoutAttr>(createIntrinsicInst( + getVoidType(), + kIROp_TupleFieldLayoutAttr, + SLANG_COUNT_OF(operands), + operands)); +} - IRPendingLayoutAttr* IRBuilder::getPendingLayoutAttr( - IRLayout* pendingLayout) - { - IRInst* operands[] = { pendingLayout }; +IRCaseTypeLayoutAttr* IRBuilder::getCaseTypeLayoutAttr(IRTypeLayout* layout) +{ + IRInst* operands[] = {layout}; - return cast<IRPendingLayoutAttr>(createIntrinsicInst( - getVoidType(), - kIROp_PendingLayoutAttr, - SLANG_COUNT_OF(operands), - operands)); - } + return cast<IRCaseTypeLayoutAttr>(createIntrinsicInst( + getVoidType(), + kIROp_CaseTypeLayoutAttr, + SLANG_COUNT_OF(operands), + operands)); +} - IRStructFieldLayoutAttr* IRBuilder::getFieldLayoutAttr( - IRInst* key, - IRVarLayout* layout) - { - IRInst* operands[] = { key, layout }; +IRSemanticAttr* IRBuilder::getSemanticAttr(IROp op, String const& name, UInt index) +{ + auto nameInst = getStringValue(name.getUnownedSlice()); + auto indexInst = getIntValue(getIntType(), index); - return cast<IRStructFieldLayoutAttr>(createIntrinsicInst( - getVoidType(), - kIROp_StructFieldLayoutAttr, - SLANG_COUNT_OF(operands), - operands)); - } + IRInst* operands[] = {nameInst, indexInst}; - IRTupleFieldLayoutAttr* IRBuilder::getTupleFieldLayoutAttr( - IRTypeLayout* layout) - { - IRInst* operands[] = { layout }; + return cast<IRSemanticAttr>( + createIntrinsicInst(getVoidType(), op, SLANG_COUNT_OF(operands), operands)); +} - return cast<IRTupleFieldLayoutAttr>(createIntrinsicInst( - getVoidType(), - kIROp_TupleFieldLayoutAttr, - SLANG_COUNT_OF(operands), - operands)); - } +IRStageAttr* IRBuilder::getStageAttr(Stage stage) +{ + auto stageInst = getIntValue(getIntType(), IRIntegerValue(stage)); + IRInst* operands[] = {stageInst}; + return cast<IRStageAttr>( + createIntrinsicInst(getVoidType(), kIROp_StageAttr, SLANG_COUNT_OF(operands), operands)); +} - IRCaseTypeLayoutAttr* IRBuilder::getCaseTypeLayoutAttr( - IRTypeLayout* layout) - { - IRInst* operands[] = { layout }; +IRAttr* IRBuilder::getAttr(IROp op, UInt operandCount, IRInst* const* operands) +{ + return cast<IRAttr>(createIntrinsicInst(getVoidType(), op, operandCount, operands)); +} - return cast<IRCaseTypeLayoutAttr>(createIntrinsicInst( - getVoidType(), - kIROp_CaseTypeLayoutAttr, - SLANG_COUNT_OF(operands), - operands)); - } - IRSemanticAttr* IRBuilder::getSemanticAttr( - IROp op, - String const& name, - UInt index) - { - auto nameInst = getStringValue(name.getUnownedSlice()); - auto indexInst = getIntValue(getIntType(), index); +IRTypeLayout* IRBuilder::getTypeLayout(IROp op, List<IRInst*> const& operands) +{ + return cast<IRTypeLayout>( + createIntrinsicInst(getVoidType(), op, operands.getCount(), operands.getBuffer())); +} - IRInst* operands[] = { nameInst, indexInst }; +IRVarLayout* IRBuilder::getVarLayout(List<IRInst*> const& operands) +{ + return cast<IRVarLayout>(createIntrinsicInst( + getVoidType(), + kIROp_VarLayout, + operands.getCount(), + operands.getBuffer())); +} + +IREntryPointLayout* IRBuilder::getEntryPointLayout( + IRVarLayout* paramsLayout, + IRVarLayout* resultLayout) +{ + IRInst* operands[] = {paramsLayout, resultLayout}; - return cast<IRSemanticAttr>(createIntrinsicInst( - getVoidType(), - op, - SLANG_COUNT_OF(operands), - operands)); - } + return cast<IREntryPointLayout>(createIntrinsicInst( + getVoidType(), + kIROp_EntryPointLayout, + SLANG_COUNT_OF(operands), + operands)); +} - IRStageAttr* IRBuilder::getStageAttr(Stage stage) - { - auto stageInst = getIntValue(getIntType(), IRIntegerValue(stage)); - IRInst* operands[] = { stageInst }; - return cast<IRStageAttr>(createIntrinsicInst( - getVoidType(), - kIROp_StageAttr, - SLANG_COUNT_OF(operands), - operands)); - } +// - IRAttr* IRBuilder::getAttr(IROp op, UInt operandCount, IRInst* const* operands) - { - return cast<IRAttr>(createIntrinsicInst( - getVoidType(), - op, - operandCount, - operands)); - } +struct IRDumpContext +{ + StringBuilder* builder = nullptr; + int indent = 0; + IRDumpOptions options; + SourceManager* sourceManager; + PathInfo lastPathInfo = PathInfo::makeUnknown(); + Dictionary<IRInst*, String> mapValueToName; + Dictionary<String, UInt> uniqueNameCounters; + UInt uniqueIDCounter = 1; +}; - IRTypeLayout* IRBuilder::getTypeLayout(IROp op, List<IRInst*> const& operands) - { - return cast<IRTypeLayout>(createIntrinsicInst( - getVoidType(), - op, - operands.getCount(), - operands.getBuffer())); - } +static void dump(IRDumpContext* context, char const* text) +{ + context->builder->append(text); +} - IRVarLayout* IRBuilder::getVarLayout(List<IRInst*> const& operands) - { - return cast<IRVarLayout>(createIntrinsicInst( - getVoidType(), - kIROp_VarLayout, - operands.getCount(), - operands.getBuffer())); - } +static void dump(IRDumpContext* context, String const& text) +{ + context->builder->append(text); +} - IREntryPointLayout* IRBuilder::getEntryPointLayout( - IRVarLayout* paramsLayout, - IRVarLayout* resultLayout) - { - IRInst* operands[] = { paramsLayout, resultLayout }; +/* +static void dump( + IRDumpContext* context, + UInt val) +{ + context->builder->append(val); +} +*/ - return cast<IREntryPointLayout>(createIntrinsicInst( - getVoidType(), - kIROp_EntryPointLayout, - SLANG_COUNT_OF(operands), - operands)); - } +static void dump(IRDumpContext* context, IntegerLiteralValue val) +{ + context->builder->append(val); +} - // +static void dump(IRDumpContext* context, FloatingPointLiteralValue val) +{ + context->builder->append(val); +} - struct IRDumpContext +static void dumpIndent(IRDumpContext* context) +{ + for (int ii = 0; ii < context->indent; ++ii) { - StringBuilder* builder = nullptr; - int indent = 0; + dump(context, "\t"); + } +} - IRDumpOptions options; - SourceManager* sourceManager; - PathInfo lastPathInfo = PathInfo::makeUnknown(); - - Dictionary<IRInst*, String> mapValueToName; - Dictionary<String, UInt> uniqueNameCounters; - UInt uniqueIDCounter = 1; - }; +bool opHasResult(IRInst* inst) +{ + auto type = inst->getDataType(); + if (!type) + return false; - static void dump( - IRDumpContext* context, - char const* text) - { - context->builder->append(text); - } + // As a bit of a hack right now, we need to check whether + // the function returns the distinguished `Void` type, + // since that is conceptually the same as "not returning + // a value." + if (type->getOp() == kIROp_VoidType) + return false; - static void dump( - IRDumpContext* context, - String const& text) - { - context->builder->append(text); - } + return true; +} - /* - static void dump( - IRDumpContext* context, - UInt val) - { - context->builder->append(val); - } - */ +bool instHasUses(IRInst* inst) +{ + return inst->firstUse != nullptr; +} - static void dump( - IRDumpContext* context, - IntegerLiteralValue val) - { - context->builder->append(val); - } +static void scrubName(String const& name, StringBuilder& sb) +{ + // Note: this function duplicates a lot of the logic + // in `EmitVisitor::scrubName`, so we should consider + // whether they can share code at some point. + // + // There is no requirement that assembly dumps and output + // code follow the same model, though, so this is just + // a nice-to-have rather than a maintenance problem + // waiting to happen. - static void dump( - IRDumpContext* context, - FloatingPointLiteralValue val) + // Allow an empty name + // Special case a name that is the empty string, just in case. + if (name.getLength() == 0) { - context->builder->append(val); + sb.append('_'); } - static void dumpIndent( - IRDumpContext* context) + int prevChar = -1; + for (auto c : name) { - for (int ii = 0; ii < context->indent; ++ii) + if (c == '.') { - dump(context, "\t"); + c = '_'; } - } - - bool opHasResult(IRInst* inst) - { - auto type = inst->getDataType(); - if (!type) return false; - - // As a bit of a hack right now, we need to check whether - // the function returns the distinguished `Void` type, - // since that is conceptually the same as "not returning - // a value." - if(type->getOp() == kIROp_VoidType) - return false; - - return true; - } - - bool instHasUses(IRInst* inst) - { - return inst->firstUse != nullptr; - } - static void scrubName( - String const& name, - StringBuilder& sb) - { - // Note: this function duplicates a lot of the logic - // in `EmitVisitor::scrubName`, so we should consider - // whether they can share code at some point. - // - // There is no requirement that assembly dumps and output - // code follow the same model, though, so this is just - // a nice-to-have rather than a maintenance problem - // waiting to happen. - - // Allow an empty name - // Special case a name that is the empty string, just in case. - if(name.getLength() == 0) + if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) { - sb.append('_'); + // Ordinary ASCII alphabetic characters are assumed + // to always be okay. } - - int prevChar = -1; - for(auto c : name) + else if ((c >= '0') && (c <= '9')) { - if(c == '.') - { - c = '_'; - } - - if(((c >= 'a') && (c <= 'z')) - || ((c >= 'A') && (c <= 'Z'))) - { - // Ordinary ASCII alphabetic characters are assumed - // to always be okay. - } - else if((c >= '0') && (c <= '9')) - { - // We don't want to allow a digit as the first - // byte in a name. - if(prevChar == -1) - { - sb.append('_'); - } - } - else + // We don't want to allow a digit as the first + // byte in a name. + if (prevChar == -1) { - // If we run into a character that wouldn't normally - // be allowed in an identifier, we need to translate - // it into something that *is* valid. - // - // Our solution for now will be very clumsy: we will - // emit `x` and then the hexadecimal version of - // the byte we were given. - sb.append("x"); - sb.append(uint32_t((unsigned char) c), 16); - - // We don't want to apply the default handling below, - // so skip to the top of the loop now. - prevChar = c; - continue; + sb.append('_'); } - - sb.append(c); - prevChar = c; } - - // If the whole thing ended with a digit, then add - // a final `_` just to make sure that we can append - // a unique ID suffix without risk of collisions. - if(('0' <= prevChar) && (prevChar <= '9')) + else { - sb.append('_'); + // If we run into a character that wouldn't normally + // be allowed in an identifier, we need to translate + // it into something that *is* valid. + // + // Our solution for now will be very clumsy: we will + // emit `x` and then the hexadecimal version of + // the byte we were given. + sb.append("x"); + sb.append(uint32_t((unsigned char)c), 16); + + // We don't want to apply the default handling below, + // so skip to the top of the loop now. + prevChar = c; + continue; } + + sb.append(c); + prevChar = c; } - static String createName( - IRDumpContext* context, - IRInst* value) + // If the whole thing ended with a digit, then add + // a final `_` just to make sure that we can append + // a unique ID suffix without risk of collisions. + if (('0' <= prevChar) && (prevChar <= '9')) { - if(auto nameHintDecoration = value->findDecoration<IRNameHintDecoration>()) - { - String nameHint = nameHintDecoration->getName(); + sb.append('_'); + } +} + +static String createName(IRDumpContext* context, IRInst* value) +{ + if (auto nameHintDecoration = value->findDecoration<IRNameHintDecoration>()) + { + String nameHint = nameHintDecoration->getName(); - StringBuilder sb; - scrubName(nameHint, sb); + StringBuilder sb; + scrubName(nameHint, sb); - String key = sb.produceString(); - UInt count = 0; - context->uniqueNameCounters.tryGetValue(key, count); + String key = sb.produceString(); + UInt count = 0; + context->uniqueNameCounters.tryGetValue(key, count); - context->uniqueNameCounters[key] = count+1; + context->uniqueNameCounters[key] = count + 1; - if(count) - { - sb.append(count); - } - return sb.produceString(); - } - else + if (count) { - StringBuilder sb; - auto id = context->uniqueIDCounter++; - sb.append(id); - return sb.produceString(); + sb.append(count); } + return sb.produceString(); } - - static String getName( - IRDumpContext* context, - IRInst* value) + else { - String name; - if (context->mapValueToName.tryGetValue(value, name)) - return name; + StringBuilder sb; + auto id = context->uniqueIDCounter++; + sb.append(id); + return sb.produceString(); + } +} - name = createName(context, value); - context->mapValueToName.add(value, name); +static String getName(IRDumpContext* context, IRInst* value) +{ + String name; + if (context->mapValueToName.tryGetValue(value, name)) return name; - } - static void dumpDebugID(IRDumpContext* context, IRInst* inst) - { + name = createName(context, value); + context->mapValueToName.add(value, name); + return name; +} + +static void dumpDebugID(IRDumpContext* context, IRInst* inst) +{ #if SLANG_ENABLE_IR_BREAK_ALLOC - if (context->options.flags & IRDumpOptions::Flag::DumpDebugIds) - { - dump(context, "{"); - dump(context, String(inst->_debugUID)); - dump(context, "}\t"); - } + if (context->options.flags & IRDumpOptions::Flag::DumpDebugIds) + { + dump(context, "{"); + dump(context, String(inst->_debugUID)); + dump(context, "}\t"); + } #else - SLANG_UNUSED(context); - SLANG_UNUSED(inst); + SLANG_UNUSED(context); + SLANG_UNUSED(inst); #endif - } +} - static void dumpID( - IRDumpContext* context, - IRInst* inst) +static void dumpID(IRDumpContext* context, IRInst* inst) +{ + if (!inst) { - if (!inst) - { - dump(context, "<null>"); - return; - } + dump(context, "<null>"); + return; + } - if( opHasResult(inst) || instHasUses(inst) ) - { - dump(context, "%"); - dump(context, getName(context, inst)); - } - else - { - dump(context, "_"); - } + if (opHasResult(inst) || instHasUses(inst)) + { + dump(context, "%"); + dump(context, getName(context, inst)); } - - static void dumpEncodeString( - IRDumpContext* context, - const UnownedStringSlice& slice) + else { - auto handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::Slang); - StringBuilder& builder = *context->builder; - StringEscapeUtil::appendQuoted(handler, slice, builder); + dump(context, "_"); } +} - static void dumpType( - IRDumpContext* context, - IRType* type); - - static bool shouldFoldInstIntoUses( - IRDumpContext* context, - IRInst* inst) - { - // Never fold an instruction into its use site - // in the "detailed" mode, so that we always - // accurately reflect the structure of the IR. - // - if(context->options.mode == IRDumpOptions::Mode::Detailed) - return false; +static void dumpEncodeString(IRDumpContext* context, const UnownedStringSlice& slice) +{ + auto handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::Slang); + StringBuilder& builder = *context->builder; + StringEscapeUtil::appendQuoted(handler, slice, builder); +} - if(as<IRConstant>(inst)) - return true; +static void dumpType(IRDumpContext* context, IRType* type); - // We are going to have a general rule that - // a type should be folded into its use site, - // which improves output in most cases, but - // we would like to not apply that rule to - // "nominal" types like `struct`s. - // - switch( inst->getOp() ) - { - case kIROp_StructType: - case kIROp_ClassType: - case kIROp_GLSLShaderStorageBufferType: - case kIROp_InterfaceType: - return false; - - default: - break; - } +static bool shouldFoldInstIntoUses(IRDumpContext* context, IRInst* inst) +{ + // Never fold an instruction into its use site + // in the "detailed" mode, so that we always + // accurately reflect the structure of the IR. + // + if (context->options.mode == IRDumpOptions::Mode::Detailed) + return false; - if(as<IRType>(inst)) - return true; + if (as<IRConstant>(inst)) + return true; - if(as<IRSPIRVAsmOperand>(inst)) - return true; + // We are going to have a general rule that + // a type should be folded into its use site, + // which improves output in most cases, but + // we would like to not apply that rule to + // "nominal" types like `struct`s. + // + switch (inst->getOp()) + { + case kIROp_StructType: + case kIROp_ClassType: + case kIROp_GLSLShaderStorageBufferType: + case kIROp_InterfaceType: return false; - return false; + default: break; } - static void dumpInst( - IRDumpContext* context, - IRInst* inst); + if (as<IRType>(inst)) + return true; - static void dumpInstBody( - IRDumpContext* context, - IRInst* inst); + if (as<IRSPIRVAsmOperand>(inst)) + return true; - static void dumpInstExpr( - IRDumpContext* context, - IRInst* inst); + return false; +} - static void dumpOperand( - IRDumpContext* context, - IRInst* inst) - { - // TODO: we should have a dedicated value for the `undef` case - if (!inst) - { - dumpID(context, inst); - return; - } +static void dumpInst(IRDumpContext* context, IRInst* inst); - if(shouldFoldInstIntoUses(context, inst)) - { - dumpInstExpr(context, inst); - return; - } +static void dumpInstBody(IRDumpContext* context, IRInst* inst); + +static void dumpInstExpr(IRDumpContext* context, IRInst* inst); +static void dumpOperand(IRDumpContext* context, IRInst* inst) +{ + // TODO: we should have a dedicated value for the `undef` case + if (!inst) + { dumpID(context, inst); + return; } - static void dumpType( - IRDumpContext* context, - IRType* type) + if (shouldFoldInstIntoUses(context, inst)) { - if (!type) - { - dump(context, "_"); - return; - } - - // TODO: we should consider some special-case printing - // for types, so that the IR doesn't get too hard to read - // (always having to back-reference for what a type expands to) - dumpOperand(context, type); + dumpInstExpr(context, inst); + return; } - static void dumpInstTypeClause( - IRDumpContext* context, - IRType* type) - { - dump(context, "\t: "); - dumpType(context, type); + dumpID(context, inst); +} +static void dumpType(IRDumpContext* context, IRType* type) +{ + if (!type) + { + dump(context, "_"); + return; } - void dumpIRDecorations( - IRDumpContext* context, - IRInst* inst) - { - for(auto dd : inst->getDecorations()) - { - dump(context, "["); - dumpInstBody(context, dd); - dump(context, "]\n"); + // TODO: we should consider some special-case printing + // for types, so that the IR doesn't get too hard to read + // (always having to back-reference for what a type expands to) + dumpOperand(context, type); +} - dumpIndent(context); - } - } +static void dumpInstTypeClause(IRDumpContext* context, IRType* type) +{ + dump(context, "\t: "); + dumpType(context, type); +} - static void dumpBlock( - IRDumpContext* context, - IRBlock* block) +void dumpIRDecorations(IRDumpContext* context, IRInst* inst) +{ + for (auto dd : inst->getDecorations()) { - context->indent--; - dumpDebugID(context, block); - dump(context, "block "); - dumpID(context, block); + dump(context, "["); + dumpInstBody(context, dd); + dump(context, "]\n"); - IRInst* inst = block->getFirstInst(); + dumpIndent(context); + } +} - // First walk through any `param` instructions, - // so that we can format them nicely - if (auto firstParam = as<IRParam, IRDynamicCastBehavior::NoUnwrap>(inst)) - { - dump(context, "(\n"); - context->indent += 2; +static void dumpBlock(IRDumpContext* context, IRBlock* block) +{ + context->indent--; + dumpDebugID(context, block); + dump(context, "block "); + dumpID(context, block); - for(;;) - { - auto param = as<IRParam, IRDynamicCastBehavior::NoUnwrap>(inst); - if (!param) - break; + IRInst* inst = block->getFirstInst(); - if (param != firstParam) - dump(context, ",\n"); + // First walk through any `param` instructions, + // so that we can format them nicely + if (auto firstParam = as<IRParam, IRDynamicCastBehavior::NoUnwrap>(inst)) + { + dump(context, "(\n"); + context->indent += 2; + + for (;;) + { + auto param = as<IRParam, IRDynamicCastBehavior::NoUnwrap>(inst); + if (!param) + break; - inst = inst->getNextInst(); + if (param != firstParam) + dump(context, ",\n"); - dumpIndent(context); - dumpIRDecorations(context, param); - dump(context, "param "); - dumpID(context, param); - dumpInstTypeClause(context, param->getFullType()); - } - context->indent -= 2; - dump(context, ")"); - } - dump(context, ":\n"); - context->indent++; + inst = inst->getNextInst(); - for(; inst; inst = inst->getNextInst()) - { - dumpInst(context, inst); + dumpIndent(context); + dumpIRDecorations(context, param); + dump(context, "param "); + dumpID(context, param); + dumpInstTypeClause(context, param->getFullType()); } + context->indent -= 2; + dump(context, ")"); } + dump(context, ":\n"); + context->indent++; - void dumpIRGlobalValueWithCode( - IRDumpContext* context, - IRGlobalValueWithCode* code) + for (; inst; inst = inst->getNextInst()) { - auto opInfo = getIROpInfo(code->getOp()); - - dumpIndent(context); - dump(context, opInfo.name); - dump(context, " "); - dumpID(context, code); + dumpInst(context, inst); + } +} - dumpInstTypeClause(context, code->getFullType()); +void dumpIRGlobalValueWithCode(IRDumpContext* context, IRGlobalValueWithCode* code) +{ + auto opInfo = getIROpInfo(code->getOp()); - if (!code->getFirstBlock()) - { - // Just a declaration. - dump(context, ";\n"); - return; - } + dumpIndent(context); + dump(context, opInfo.name); + dump(context, " "); + dumpID(context, code); - dump(context, "\n"); + dumpInstTypeClause(context, code->getFullType()); - dumpIndent(context); - dump(context, "{\n"); - context->indent++; + if (!code->getFirstBlock()) + { + // Just a declaration. + dump(context, ";\n"); + return; + } - for (auto bb = code->getFirstBlock(); bb; bb = bb->getNextBlock()) - { - if (bb != code->getFirstBlock()) - dump(context, "\n"); - dumpBlock(context, bb); - } + dump(context, "\n"); - context->indent--; - dump(context, "}"); - } + dumpIndent(context); + dump(context, "{\n"); + context->indent++; - static void dumpInstOperandList( - IRDumpContext* context, - IRInst* inst) + for (auto bb = code->getFirstBlock(); bb; bb = bb->getNextBlock()) { - UInt argCount = inst->getOperandCount(); + if (bb != code->getFirstBlock()) + dump(context, "\n"); + dumpBlock(context, bb); + } - if (argCount == 0) - return; + context->indent--; + dump(context, "}"); +} - UInt ii = 0; +static void dumpInstOperandList(IRDumpContext* context, IRInst* inst) +{ + UInt argCount = inst->getOperandCount(); - // Special case: make printing of `call` a bit - // nicer to look at - if (inst->getOp() == kIROp_Call && argCount > 0) - { - dump(context, " "); - auto argVal = inst->getOperand(ii++); - dumpOperand(context, argVal); - } + if (argCount == 0) + return; - bool first = true; - dump(context, "("); - for (; ii < argCount; ++ii) - { - if (!first) - dump(context, ", "); + UInt ii = 0; - auto argVal = inst->getOperand(ii); + // Special case: make printing of `call` a bit + // nicer to look at + if (inst->getOp() == kIROp_Call && argCount > 0) + { + dump(context, " "); + auto argVal = inst->getOperand(ii++); + dumpOperand(context, argVal); + } - dumpOperand(context, argVal); + bool first = true; + dump(context, "("); + for (; ii < argCount; ++ii) + { + if (!first) + dump(context, ", "); - first = false; - } + auto argVal = inst->getOperand(ii); - dump(context, ")"); - } + dumpOperand(context, argVal); - void dumpIRWitnessTableEntry( - IRDumpContext* context, - IRWitnessTableEntry* entry) - { - dump(context, "witness_table_entry("); - dumpOperand(context, entry->requirementKey.get()); - dump(context, ","); - dumpOperand(context, entry->satisfyingVal.get()); - dump(context, ")\n"); + first = false; } - void dumpIRParentInst( - IRDumpContext* context, - IRInst* inst) - { - auto opInfo = getIROpInfo(inst->getOp()); + dump(context, ")"); +} - dump(context, opInfo.name); - dump(context, " "); - dumpID(context, inst); +void dumpIRWitnessTableEntry(IRDumpContext* context, IRWitnessTableEntry* entry) +{ + dump(context, "witness_table_entry("); + dumpOperand(context, entry->requirementKey.get()); + dump(context, ","); + dumpOperand(context, entry->satisfyingVal.get()); + dump(context, ")\n"); +} + +void dumpIRParentInst(IRDumpContext* context, IRInst* inst) +{ + auto opInfo = getIROpInfo(inst->getOp()); - dumpInstTypeClause(context, inst->getFullType()); + dump(context, opInfo.name); + dump(context, " "); + dumpID(context, inst); - dumpInstOperandList(context, inst); + dumpInstTypeClause(context, inst->getFullType()); - if (!inst->getFirstChild()) - { - // Empty. - dump(context, ";\n"); - return; - } + dumpInstOperandList(context, inst); - dump(context, "\n"); + if (!inst->getFirstChild()) + { + // Empty. + dump(context, ";\n"); + return; + } - dumpIndent(context); - dump(context, "{\n"); - context->indent++; + dump(context, "\n"); - for(auto child : inst->getChildren()) - { - dumpInst(context, child); - } + dumpIndent(context); + dump(context, "{\n"); + context->indent++; - context->indent--; - dumpIndent(context); - dump(context, "}\n"); + for (auto child : inst->getChildren()) + { + dumpInst(context, child); } - void dumpIRGeneric( - IRDumpContext* context, - IRGeneric* witnessTable) + context->indent--; + dumpIndent(context); + dump(context, "}\n"); +} + +void dumpIRGeneric(IRDumpContext* context, IRGeneric* witnessTable) +{ + dump(context, "\n"); + dumpIndent(context); + dump(context, "ir_witness_table "); + dumpID(context, witnessTable); + dump(context, "\n{\n"); + context->indent++; + + for (auto ii : witnessTable->getChildren()) { - dump(context, "\n"); - dumpIndent(context); - dump(context, "ir_witness_table "); - dumpID(context, witnessTable); - dump(context, "\n{\n"); - context->indent++; + dumpInst(context, ii); + } - for (auto ii : witnessTable->getChildren()) - { - dumpInst(context, ii); - } + context->indent--; + dump(context, "}\n"); +} - context->indent--; - dump(context, "}\n"); +static void dumpInstExpr(IRDumpContext* context, IRInst* inst) +{ + if (!inst) + { + dump(context, "<null>"); + return; } - static void dumpInstExpr( - IRDumpContext* context, - IRInst* inst) + auto op = inst->getOp(); + auto opInfo = getIROpInfo(op); + + // Special-case the literal instructions. + if (auto irConst = as<IRConstant>(inst)) { - if (!inst) + switch (op) { - dump(context, "<null>"); + case kIROp_IntLit: + dump(context, irConst->value.intVal); + dump(context, " : "); + dumpType(context, irConst->getFullType()); return; - } - auto op = inst->getOp(); - auto opInfo = getIROpInfo(op); + case kIROp_FloatLit: + dump(context, irConst->value.floatVal); + dump(context, " : "); + dumpType(context, irConst->getFullType()); + return; - // Special-case the literal instructions. - if(auto irConst = as<IRConstant>(inst)) - { - switch (op) - { - case kIROp_IntLit: - dump(context, irConst->value.intVal); - dump(context, " : "); - dumpType(context, irConst->getFullType()); - return; - - case kIROp_FloatLit: - dump(context, irConst->value.floatVal); - dump(context, " : "); - dumpType(context, irConst->getFullType()); - return; - - case kIROp_BoolLit: - dump(context, irConst->value.intVal ? "true" : "false"); - return; - - case kIROp_BlobLit: - dump(context, "<binary blob>"); - return; - - case kIROp_StringLit: - dumpEncodeString(context, irConst->getStringSlice()); - return; - - case kIROp_PtrLit: - dump(context, "<ptr>"); - return; - - default: - break; - } - } + case kIROp_BoolLit: dump(context, irConst->value.intVal ? "true" : "false"); return; - // Special case the SPIR-V asm operands as the distinction here is - // clear anyway to the user - switch(op) - { - case kIROp_SPIRVAsmOperandEnum: - dumpInstExpr(context, inst->getOperand(0)); - return; - case kIROp_SPIRVAsmOperandLiteral: - dumpInstExpr(context, inst->getOperand(0)); - return; - case kIROp_SPIRVAsmOperandInst: - dumpInstExpr(context, inst->getOperand(0)); - return; - case kIROp_SPIRVAsmOperandRayPayloadFromLocation: - dump(context, "__rayPayloadFromLocation("); - dumpInstExpr(context, inst->getOperand(0)); - dump(context, ")"); - return; - case kIROp_SPIRVAsmOperandRayAttributeFromLocation: - dump(context, "__rayAttributeFromLocation("); - dumpInstExpr(context, inst->getOperand(0)); - dump(context, ")"); - return; - case kIROp_SPIRVAsmOperandRayCallableFromLocation: - dump(context, "__rayCallableFromLocation("); - dumpInstExpr(context, inst->getOperand(0)); - dump(context, ")"); - return; - case kIROp_SPIRVAsmOperandId: - dump(context, "%"); - dumpInstExpr(context, inst->getOperand(0)); - return; - case kIROp_SPIRVAsmOperandResult: - dump(context, "result"); - return; - case kIROp_SPIRVAsmOperandTruncate: - dump(context, "__truncate"); - return; - case kIROp_SPIRVAsmOperandSampledType: - dump(context, "__sampledType("); - dumpInstExpr(context, inst->getOperand(0)); - dump(context, ")"); - return; - case kIROp_SPIRVAsmOperandImageType: - dump(context, "__imageType("); - dumpInstExpr(context, inst->getOperand(0)); - dump(context, ")"); - return; - case kIROp_SPIRVAsmOperandSampledImageType: - dump(context, "__sampledImageType("); - dumpInstExpr(context, inst->getOperand(0)); - dump(context, ")"); - return; - } + case kIROp_BlobLit: dump(context, "<binary blob>"); return; + + case kIROp_StringLit: dumpEncodeString(context, irConst->getStringSlice()); return; + + case kIROp_PtrLit: dump(context, "<ptr>"); return; - dump(context, opInfo.name); - dumpInstOperandList(context, inst); + default: break; + } } - static void dumpInstBody( - IRDumpContext* context, - IRInst* inst) + // Special case the SPIR-V asm operands as the distinction here is + // clear anyway to the user + switch (op) { - if (!inst) - { - dump(context, "<null>"); - return; - } + case kIROp_SPIRVAsmOperandEnum: dumpInstExpr(context, inst->getOperand(0)); return; + case kIROp_SPIRVAsmOperandLiteral: dumpInstExpr(context, inst->getOperand(0)); return; + case kIROp_SPIRVAsmOperandInst: dumpInstExpr(context, inst->getOperand(0)); return; + case kIROp_SPIRVAsmOperandRayPayloadFromLocation: + dump(context, "__rayPayloadFromLocation("); + dumpInstExpr(context, inst->getOperand(0)); + dump(context, ")"); + return; + case kIROp_SPIRVAsmOperandRayAttributeFromLocation: + dump(context, "__rayAttributeFromLocation("); + dumpInstExpr(context, inst->getOperand(0)); + dump(context, ")"); + return; + case kIROp_SPIRVAsmOperandRayCallableFromLocation: + dump(context, "__rayCallableFromLocation("); + dumpInstExpr(context, inst->getOperand(0)); + dump(context, ")"); + return; + case kIROp_SPIRVAsmOperandId: + dump(context, "%"); + dumpInstExpr(context, inst->getOperand(0)); + return; + case kIROp_SPIRVAsmOperandResult: dump(context, "result"); return; + case kIROp_SPIRVAsmOperandTruncate: dump(context, "__truncate"); return; + case kIROp_SPIRVAsmOperandSampledType: + dump(context, "__sampledType("); + dumpInstExpr(context, inst->getOperand(0)); + dump(context, ")"); + return; + case kIROp_SPIRVAsmOperandImageType: + dump(context, "__imageType("); + dumpInstExpr(context, inst->getOperand(0)); + dump(context, ")"); + return; + case kIROp_SPIRVAsmOperandSampledImageType: + dump(context, "__sampledImageType("); + dumpInstExpr(context, inst->getOperand(0)); + dump(context, ")"); + return; + } - auto op = inst->getOp(); + dump(context, opInfo.name); + dumpInstOperandList(context, inst); +} - dumpIRDecorations(context, inst); +static void dumpInstBody(IRDumpContext* context, IRInst* inst) +{ + if (!inst) + { + dump(context, "<null>"); + return; + } - dumpDebugID(context, inst); + auto op = inst->getOp(); - // There are several ops we want to special-case here, - // so that they will be more pleasant to look at. - // - switch (op) - { - case kIROp_Func: - case kIROp_GlobalVar: - case kIROp_Generic: - case kIROp_Expand: - dumpIRGlobalValueWithCode(context, (IRGlobalValueWithCode*)inst); - return; + dumpIRDecorations(context, inst); - case kIROp_WitnessTable: - case kIROp_StructType: - case kIROp_ClassType: - case kIROp_GLSLShaderStorageBufferType: - case kIROp_SPIRVAsm: - dumpIRParentInst(context, inst); - return; + dumpDebugID(context, inst); - case kIROp_WitnessTableEntry: - dumpIRWitnessTableEntry(context, (IRWitnessTableEntry*)inst); - return; + // There are several ops we want to special-case here, + // so that they will be more pleasant to look at. + // + switch (op) + { + case kIROp_Func: + case kIROp_GlobalVar: + case kIROp_Generic: + case kIROp_Expand: dumpIRGlobalValueWithCode(context, (IRGlobalValueWithCode*)inst); return; - default: - break; - } + case kIROp_WitnessTable: + case kIROp_StructType: + case kIROp_ClassType: + case kIROp_GLSLShaderStorageBufferType: + case kIROp_SPIRVAsm: dumpIRParentInst(context, inst); return; - // Okay, we have a seemingly "ordinary" op now - auto dataType = inst->getDataType(); - auto rate = inst->getRate(); + case kIROp_WitnessTableEntry: + dumpIRWitnessTableEntry(context, (IRWitnessTableEntry*)inst); + return; - if(rate) - { - dump(context, "@"); - dumpOperand(context, rate); - dump(context, " "); - } + default: break; + } - if(opHasResult(inst) || instHasUses(inst)) - { - dump(context, "let "); - dumpID(context, inst); - dumpInstTypeClause(context, dataType); - dump(context, "\t= "); - } - else - { - // No result, okay... - } + // Okay, we have a seemingly "ordinary" op now + auto dataType = inst->getDataType(); + auto rate = inst->getRate(); - dumpInstExpr(context, inst); + if (rate) + { + dump(context, "@"); + dumpOperand(context, rate); + dump(context, " "); } - static void dumpInst( - IRDumpContext* context, - IRInst* inst) + if (opHasResult(inst) || instHasUses(inst)) { - if(shouldFoldInstIntoUses(context, inst)) - return; + dump(context, "let "); + dumpID(context, inst); + dumpInstTypeClause(context, dataType); + dump(context, "\t= "); + } + else + { + // No result, okay... + } - dumpIndent(context); - dumpInstBody(context, inst); + dumpInstExpr(context, inst); +} + +static void dumpInst(IRDumpContext* context, IRInst* inst) +{ + if (shouldFoldInstIntoUses(context, inst)) + return; - // Output the originating source location + dumpIndent(context); + dumpInstBody(context, inst); + + // Output the originating source location + { + SourceManager* sourceManager = context->sourceManager; + if (sourceManager && context->options.flags & IRDumpOptions::Flag::SourceLocations) { - SourceManager* sourceManager = context->sourceManager; - if (sourceManager && context->options.flags & IRDumpOptions::Flag::SourceLocations) + StringBuilder buf; + buf << " loc: "; + + // Output the line number information + if (inst->sourceLoc.isValid()) { - StringBuilder buf; - buf << " loc: "; + // Might want to output actual, but nominal is okay for default + const SourceLocType sourceLocType = SourceLocType::Nominal; - // Output the line number information - if (inst->sourceLoc.isValid()) + // Get the source location + const HumaneSourceLoc humaneLoc = + sourceManager->getHumaneLoc(inst->sourceLoc, sourceLocType); + if (humaneLoc.line >= 0) { - // Might want to output actual, but nominal is okay for default - const SourceLocType sourceLocType = SourceLocType::Nominal; + buf << humaneLoc.line << "," << humaneLoc.column; - // Get the source location - const HumaneSourceLoc humaneLoc = sourceManager->getHumaneLoc(inst->sourceLoc, sourceLocType); - if (humaneLoc.line >= 0) - { - buf << humaneLoc.line << "," << humaneLoc.column; - - if (humaneLoc.pathInfo != context->lastPathInfo) - { - buf << " "; - // Output the the location - humaneLoc.pathInfo.appendDisplayName(buf); - context->lastPathInfo = humaneLoc.pathInfo; - } - } - else + if (humaneLoc.pathInfo != context->lastPathInfo) { - buf << "not found"; + buf << " "; + // Output the the location + humaneLoc.pathInfo.appendDisplayName(buf); + context->lastPathInfo = humaneLoc.pathInfo; } } else { - buf << "na"; + buf << "not found"; } - - dump(context, buf.getUnownedSlice()); } - } + else + { + buf << "na"; + } - dump(context, "\n"); + dump(context, buf.getUnownedSlice()); + } } - void dumpIRModule( - IRDumpContext* context, - IRModule* module) + dump(context, "\n"); +} + +void dumpIRModule(IRDumpContext* context, IRModule* module) +{ + for (auto ii : module->getGlobalInsts()) { - for(auto ii : module->getGlobalInsts()) - { - dumpInst(context, ii); - } + dumpInst(context, ii); } +} - void printSlangIRAssembly(StringBuilder& builder, IRModule* module, const IRDumpOptions& options, SourceManager* sourceManager) +void printSlangIRAssembly( + StringBuilder& builder, + IRModule* module, + const IRDumpOptions& options, + SourceManager* sourceManager) +{ + IRDumpContext context; + context.builder = &builder; + context.indent = 0; + context.options = options; + context.sourceManager = sourceManager; + + dumpIRModule(&context, module); +} + +void dumpIR( + IRInst* globalVal, + const IRDumpOptions& options, + SourceManager* sourceManager, + ISlangWriter* writer) +{ + StringBuilder sb; + + IRDumpContext context; + context.builder = &sb; + context.indent = 0; + context.options = options; + context.sourceManager = sourceManager; + + if (globalVal->getOp() == kIROp_Module) + dumpIRModule(&context, globalVal->getModule()); + else + dumpInst(&context, globalVal); + + writer->write(sb.getBuffer(), sb.getLength()); + writer->flush(); +} + +void dumpIR( + IRModule* module, + const IRDumpOptions& options, + char const* label, + SourceManager* sourceManager, + ISlangWriter* inWriter) +{ + WriterHelper writer(inWriter); + + if (label) { - IRDumpContext context; - context.builder = &builder; - context.indent = 0; - context.options = options; - context.sourceManager = sourceManager; - - dumpIRModule(&context, module); + writer.put("### "); + writer.put(label); + writer.put(":\n"); } - void dumpIR(IRInst* globalVal, const IRDumpOptions& options, SourceManager* sourceManager, ISlangWriter* writer) + dumpIR(module, options, sourceManager, inWriter); + + if (label) { - StringBuilder sb; + writer.put("###\n"); + } +} - IRDumpContext context; - context.builder = &sb; - context.indent = 0; - context.options = options; - context.sourceManager = sourceManager; +String getSlangIRAssembly( + IRModule* module, + const IRDumpOptions& options, + SourceManager* sourceManager) +{ + StringBuilder sb; + printSlangIRAssembly(sb, module, options, sourceManager); + return sb; +} + +void dumpIR( + IRModule* module, + const IRDumpOptions& options, + SourceManager* sourceManager, + ISlangWriter* writer) +{ + String ir = getSlangIRAssembly(module, options, sourceManager); + writer->write(ir.getBuffer(), ir.getLength()); + writer->flush(); +} - if (globalVal->getOp() == kIROp_Module) - dumpIRModule(&context, globalVal->getModule()); - else - dumpInst(&context, globalVal); +// Pre-declare +static bool _isTypeOperandEqual(IRInst* a, IRInst* b); - writer->write(sb.getBuffer(), sb.getLength()); - writer->flush(); +static bool _areTypeOperandsEqual(IRInst* a, IRInst* b) +{ + // Must have same number of operands + const auto operandCountA = Index(a->getOperandCount()); + if (operandCountA != Index(b->getOperandCount())) + { + return false; } - void dumpIR(IRModule* module, const IRDumpOptions& options, char const* label, SourceManager* sourceManager, ISlangWriter* inWriter) + // All the operands must be equal + for (Index i = 0; i < operandCountA; ++i) { - WriterHelper writer(inWriter); + IRInst* operandA = a->getOperand(i); + IRInst* operandB = b->getOperand(i); - if (label) + if (!_isTypeOperandEqual(operandA, operandB)) { - writer.put("### "); - writer.put(label); - writer.put(":\n"); + return false; } + } - dumpIR(module, options, sourceManager, inWriter); + return true; +} - if (label) +bool isNominalOp(IROp op) +{ + // True if the op identity is 'nominal' + switch (op) + { + case kIROp_StructType: + case kIROp_ClassType: + case kIROp_GLSLShaderStorageBufferType: + case kIROp_InterfaceType: + case kIROp_Generic: + case kIROp_Param: { - writer.put("###\n"); + return true; } } + return false; +} - String getSlangIRAssembly(IRModule* module, const IRDumpOptions& options, SourceManager* sourceManager) +// True if a type operand is equal. Operands are 'IRInst' - but it's only a restricted set that +// can be operands of IRType instructions +static bool _isTypeOperandEqual(IRInst* a, IRInst* b) +{ + if (a == b) { - StringBuilder sb; - printSlangIRAssembly(sb, module, options, sourceManager); - return sb; + return true; } - void dumpIR(IRModule* module, const IRDumpOptions& options, SourceManager* sourceManager, ISlangWriter* writer) + if (a == nullptr || b == nullptr) { - String ir = getSlangIRAssembly(module, options, sourceManager); - writer->write(ir.getBuffer(), ir.getLength()); - writer->flush(); + return false; } - // Pre-declare - static bool _isTypeOperandEqual(IRInst* a, IRInst* b); + const IROp opA = IROp(a->getOp() & kIROpMask_OpMask); + const IROp opB = IROp(b->getOp() & kIROpMask_OpMask); - static bool _areTypeOperandsEqual(IRInst* a, IRInst* b) + if (opA != opB) { - // Must have same number of operands - const auto operandCountA = Index(a->getOperandCount()); - if (operandCountA != Index(b->getOperandCount())) - { - return false; - } - - // All the operands must be equal - for (Index i = 0; i < operandCountA; ++i) - { - IRInst* operandA = a->getOperand(i); - IRInst* operandB = b->getOperand(i); - - if (!_isTypeOperandEqual(operandA, operandB)) - { - return false; - } - } - - return true; + return false; } - bool isNominalOp(IROp op) + // If the type is nominal - it can only be the same if the pointer is the same. + if (isNominalOp(opA)) { - // True if the op identity is 'nominal' - switch (op) - { - case kIROp_StructType: - case kIROp_ClassType: - case kIROp_GLSLShaderStorageBufferType: - case kIROp_InterfaceType: - case kIROp_Generic: - case kIROp_Param: - { - return true; - } - } + // The pointer isn't the same (as that was already tested), so cannot be equal return false; } - // True if a type operand is equal. Operands are 'IRInst' - but it's only a restricted set that - // can be operands of IRType instructions - static bool _isTypeOperandEqual(IRInst* a, IRInst* b) + // Both are types + if (IRType::isaImpl(opA)) { - if (a == b) + if (IRBasicType::isaImpl(opA)) { + // If it's a basic type, then their op being the same means we are done return true; } - if (a == nullptr || b == nullptr) - { - return false; - } - - const IROp opA = IROp(a->getOp() & kIROpMask_OpMask); - const IROp opB = IROp(b->getOp() & kIROpMask_OpMask); - - if (opA != opB) - { - return false; - } - - // If the type is nominal - it can only be the same if the pointer is the same. - if (isNominalOp(opA)) - { - // The pointer isn't the same (as that was already tested), so cannot be equal - return false; - } - - // Both are types - if (IRType::isaImpl(opA)) - { - if (IRBasicType::isaImpl(opA)) - { - // If it's a basic type, then their op being the same means we are done - return true; - } - - // We don't care about the parent or positioning - // We also don't care about 'type' - because these instructions are defining the type. - // - // We may want to care about decorations. - - // TODO(JS): There is a question here about what to do about decorations. - // For now we ignore decorations. Are two types potentially different if there decorations different? - // If decorations play a part in difference in types - the order of decorations presumably is not important. + // We don't care about the parent or positioning + // We also don't care about 'type' - because these instructions are defining the type. + // + // We may want to care about decorations. - // All the operands of the types must be equal - return _areTypeOperandsEqual(a, b); - } - - // If it's a constant... - if (IRConstant::isaImpl(opA)) - { - // TODO: This is contrived in that we want two types that are the same, but are different - // pointers to match here. - // If we make getHashCode for IRType* compatible with isTypeEqual, then we should probably use that. - return static_cast<IRConstant*>(a)->isValueEqual(static_cast<IRConstant*>(b)) && - isTypeEqual(a->getFullType(), b->getFullType()); - } - if (IRSpecialize::isaImpl(opA) || opA == kIROp_LookupWitness) - { - return _areTypeOperandsEqual(a, b); - } - SLANG_ASSERT(!"Unhandled comparison"); + // TODO(JS): There is a question here about what to do about decorations. + // For now we ignore decorations. Are two types potentially different if there decorations + // different? If decorations play a part in difference in types - the order of decorations + // presumably is not important. - // We can't equate any other type.. - return false; + // All the operands of the types must be equal + return _areTypeOperandsEqual(a, b); } - bool isTypeEqual(IRType* a, IRType* b) + // If it's a constant... + if (IRConstant::isaImpl(opA)) + { + // TODO: This is contrived in that we want two types that are the same, but are different + // pointers to match here. + // If we make getHashCode for IRType* compatible with isTypeEqual, then we should probably + // use that. + return static_cast<IRConstant*>(a)->isValueEqual(static_cast<IRConstant*>(b)) && + isTypeEqual(a->getFullType(), b->getFullType()); + } + if (IRSpecialize::isaImpl(opA) || opA == kIROp_LookupWitness) { - // _isTypeOperandEqual handles comparison of types so can defer to it - return _isTypeOperandEqual(a, b); + return _areTypeOperandsEqual(a, b); } + SLANG_ASSERT(!"Unhandled comparison"); + + // We can't equate any other type.. + return false; +} - bool isIntegralType(IRType *t) +bool isTypeEqual(IRType* a, IRType* b) +{ + // _isTypeOperandEqual handles comparison of types so can defer to it + return _isTypeOperandEqual(a, b); +} + +bool isIntegralType(IRType* t) +{ + if (auto basic = as<IRBasicType>(t)) { - if(auto basic = as<IRBasicType>(t)) + switch (basic->getBaseType()) { - switch(basic->getBaseType()) - { - case BaseType::Int8: - case BaseType::Int16: - case BaseType::Int: - case BaseType::Int64: - case BaseType::UInt8: - case BaseType::UInt16: - case BaseType::UInt: - case BaseType::UInt64: - case BaseType::IntPtr: - case BaseType::UIntPtr: - return true; - default: - return false; - } + case BaseType::Int8: + case BaseType::Int16: + case BaseType::Int: + case BaseType::Int64: + case BaseType::UInt8: + case BaseType::UInt16: + case BaseType::UInt: + case BaseType::UInt64: + case BaseType::IntPtr: + case BaseType::UIntPtr: return true; + default: return false; } - return false; } + return false; +} - bool isFloatingType(IRType *t) +bool isFloatingType(IRType* t) +{ + if (auto basic = as<IRBasicType>(t)) { - if(auto basic = as<IRBasicType>(t)) + switch (basic->getBaseType()) { - switch(basic->getBaseType()) - { - case BaseType::Float: - case BaseType::Half: - case BaseType::Double: - return true; - default: - return false; - } + case BaseType::Float: + case BaseType::Half: + case BaseType::Double: return true; + default: return false; } - return false; } + return false; +} - IntInfo getIntTypeInfo(const IRType* intType) +IntInfo getIntTypeInfo(const IRType* intType) +{ + switch (intType->getOp()) { - switch(intType->getOp()) - { - case kIROp_UInt8Type: return {8, false}; - case kIROp_UInt16Type: return {16, false}; - case kIROp_UIntType: return {32, false}; - case kIROp_UInt64Type: return {64, false}; - case kIROp_Int8Type: return {8, true}; - case kIROp_Int16Type: return {16, true}; - case kIROp_IntType: return {32, true}; - case kIROp_Int64Type: return {64, true}; - - case kIROp_IntPtrType: // target platform dependent - case kIROp_UIntPtrType: // target platform dependent - default: - SLANG_UNEXPECTED("Unhandled type passed to getIntTypeInfo"); - } + case kIROp_UInt8Type: return {8, false}; + case kIROp_UInt16Type: return {16, false}; + case kIROp_UIntType: return {32, false}; + case kIROp_UInt64Type: return {64, false}; + case kIROp_Int8Type: return {8, true}; + case kIROp_Int16Type: return {16, true}; + case kIROp_IntType: return {32, true}; + case kIROp_Int64Type: return {64, true}; + + case kIROp_IntPtrType: // target platform dependent + case kIROp_UIntPtrType: // target platform dependent + default: SLANG_UNEXPECTED("Unhandled type passed to getIntTypeInfo"); } +} - IROp getIntTypeOpFromInfo(const IntInfo info) +IROp getIntTypeOpFromInfo(const IntInfo info) +{ + switch (info.width) { - switch(info.width) - { - case 8: return info.isSigned ? kIROp_Int8Type : kIROp_UInt8Type; - case 16: return info.isSigned ? kIROp_Int16Type : kIROp_UInt16Type; - case 32: return info.isSigned ? kIROp_IntType : kIROp_UIntType; - case 64: return info.isSigned ? kIROp_Int64Type : kIROp_UInt64Type; - default: - SLANG_UNEXPECTED("Unhandled info passed to getIntTypeOpFromInfo"); - } + case 8: return info.isSigned ? kIROp_Int8Type : kIROp_UInt8Type; + case 16: return info.isSigned ? kIROp_Int16Type : kIROp_UInt16Type; + case 32: return info.isSigned ? kIROp_IntType : kIROp_UIntType; + case 64: return info.isSigned ? kIROp_Int64Type : kIROp_UInt64Type; + default: SLANG_UNEXPECTED("Unhandled info passed to getIntTypeOpFromInfo"); } +} - FloatInfo getFloatingTypeInfo(const IRType* floatType) +FloatInfo getFloatingTypeInfo(const IRType* floatType) +{ + switch (floatType->getOp()) { - switch(floatType->getOp()) - { - case kIROp_HalfType: return {16}; - case kIROp_FloatType: return {32}; - case kIROp_DoubleType: return {64}; - default: - SLANG_UNEXPECTED("Unhandled type passed to getFloatTypeInfo"); - } + case kIROp_HalfType: return {16}; + case kIROp_FloatType: return {32}; + case kIROp_DoubleType: return {64}; + default: SLANG_UNEXPECTED("Unhandled type passed to getFloatTypeInfo"); } +} - bool isIntegralScalarOrCompositeType(IRType* t) +bool isIntegralScalarOrCompositeType(IRType* t) +{ + if (!t) + return false; + switch (t->getOp()) { - if (!t) - return false; - switch (t->getOp()) - { - case kIROp_VectorType: - case kIROp_MatrixType: - return isIntegralType((IRType*)t->getOperand(0)); - default: - return isIntegralType(t); - } + case kIROp_VectorType: + case kIROp_MatrixType: return isIntegralType((IRType*)t->getOperand(0)); + default: return isIntegralType(t); } +} - IRStructField* findStructField(IRInst* type, IRStructKey* key) +IRStructField* findStructField(IRInst* type, IRStructKey* key) +{ + if (auto irStructType = as<IRStructType>(type)) { - if (auto irStructType = as<IRStructType>(type)) + for (auto field : irStructType->getFields()) { - for (auto field : irStructType->getFields()) + if (field->getKey() == key) { - if (field->getKey() == key) - { - return field; - } + return field; } } - else if (auto irSpecialize = as<IRSpecialize>(type)) + } + else if (auto irSpecialize = as<IRSpecialize>(type)) + { + if (auto irGeneric = as<IRGeneric>(irSpecialize->getBase())) { - if (auto irGeneric = as<IRGeneric>(irSpecialize->getBase())) + if (auto irGenericStructType = + as<IRStructType>(findInnerMostGenericReturnVal(irGeneric))) { - if (auto irGenericStructType = as<IRStructType>(findInnerMostGenericReturnVal(irGeneric))) - { - return findStructField(irGenericStructType, key); - } + return findStructField(irGenericStructType, key); } } - - return nullptr; } - void findAllInstsBreadthFirst(IRInst* inst, List<IRInst*>& outInsts) - { - Index index = outInsts.getCount(); + return nullptr; +} - outInsts.add(inst); +void findAllInstsBreadthFirst(IRInst* inst, List<IRInst*>& outInsts) +{ + Index index = outInsts.getCount(); - while (index < outInsts.getCount()) - { - IRInst* cur = outInsts[index++]; + outInsts.add(inst); - IRInstListBase childrenList = cur->getDecorationsAndChildren(); - for (IRInst* child : childrenList) - { - outInsts.add(child); - } + while (index < outInsts.getCount()) + { + IRInst* cur = outInsts[index++]; + + IRInstListBase childrenList = cur->getDecorationsAndChildren(); + for (IRInst* child : childrenList) + { + outInsts.add(child); } } +} - IRDecoration* IRInst::getFirstDecoration() - { - return as<IRDecoration>(getFirstDecorationOrChild()); - } +IRDecoration* IRInst::getFirstDecoration() +{ + return as<IRDecoration>(getFirstDecorationOrChild()); +} - IRDecoration* IRInst::getLastDecoration() - { - IRDecoration* decoration = getFirstDecoration(); - if (!decoration) return nullptr; +IRDecoration* IRInst::getLastDecoration() +{ + IRDecoration* decoration = getFirstDecoration(); + if (!decoration) + return nullptr; - while (auto nextDecoration = decoration->getNextDecoration()) - decoration = nextDecoration; + while (auto nextDecoration = decoration->getNextDecoration()) + decoration = nextDecoration; - return decoration; - } + return decoration; +} - IRInstList<IRDecoration> IRInst::getDecorations() - { - return IRInstList<IRDecoration>( - getFirstDecoration(), - getLastDecoration()); - } +IRInstList<IRDecoration> IRInst::getDecorations() +{ + return IRInstList<IRDecoration>(getFirstDecoration(), getLastDecoration()); +} - IRInst* IRInst::getFirstChild() - { - // The children come after any decorations, - // so if there are any decorations, then the - // first child is right after the last decoration. - // - if(auto lastDecoration = getLastDecoration()) - return lastDecoration->getNextInst(); - // - // Otherwise, there must be no decorations, so - // that the first "child or decoration" is a child. - // - return getFirstDecorationOrChild(); - } +IRInst* IRInst::getFirstChild() +{ + // The children come after any decorations, + // so if there are any decorations, then the + // first child is right after the last decoration. + // + if (auto lastDecoration = getLastDecoration()) + return lastDecoration->getNextInst(); + // + // Otherwise, there must be no decorations, so + // that the first "child or decoration" is a child. + // + return getFirstDecorationOrChild(); +} - IRInst* IRInst::getLastChild() - { - // The children come after any decorations, so - // that the last item in the list of children - // and decorations is the last child *unless* - // it is a decoration, in which case there are - // no children. - // - auto lastChild = getLastDecorationOrChild(); - return as<IRDecoration>(lastChild) ? nullptr : lastChild; - } +IRInst* IRInst::getLastChild() +{ + // The children come after any decorations, so + // that the last item in the list of children + // and decorations is the last child *unless* + // it is a decoration, in which case there are + // no children. + // + auto lastChild = getLastDecorationOrChild(); + return as<IRDecoration>(lastChild) ? nullptr : lastChild; +} - IRRate* IRInst::getRate() - { - if(auto rateQualifiedType = as<IRRateQualifiedType>(getFullType())) - return rateQualifiedType->getRate(); +IRRate* IRInst::getRate() +{ + if (auto rateQualifiedType = as<IRRateQualifiedType>(getFullType())) + return rateQualifiedType->getRate(); - return nullptr; - } + return nullptr; +} - IRType* IRInst::getDataType() - { - auto type = getFullType(); - if(auto rateQualifiedType = as<IRRateQualifiedType>(type)) - return rateQualifiedType->getValueType(); +IRType* IRInst::getDataType() +{ + auto type = getFullType(); + if (auto rateQualifiedType = as<IRRateQualifiedType>(type)) + return rateQualifiedType->getValueType(); - return type; - } + return type; +} - void validateIRInstOperands(IRInst*); +void validateIRInstOperands(IRInst*); - - // Returns true if `instToCheck` is defined after `otherInst`. - static bool _isInstDefinedAfter(IRInst* instToCheck, IRInst* otherInst) + +// Returns true if `instToCheck` is defined after `otherInst`. +static bool _isInstDefinedAfter(IRInst* instToCheck, IRInst* otherInst) +{ + for (auto inst = otherInst->getNextInst(); inst; inst = inst->getNextInst()) { - for (auto inst = otherInst->getNextInst(); inst; inst = inst->getNextInst()) - { - if (inst == instToCheck) - return true; - } - return false; + if (inst == instToCheck) + return true; } + return false; +} - static void _maybeHoistOperand(IRUse* use) +static void _maybeHoistOperand(IRUse* use) +{ + ShortList<IRUse*, 16> workList1, workList2; + workList1.add(use); + while (workList1.getCount()) { - ShortList<IRUse*, 16> workList1, workList2; - workList1.add(use); - while (workList1.getCount()) + for (auto item : workList1) { - for (auto item : workList1) - { - auto user = item->getUser(); - auto operand = item->get(); - if (!operand) - continue; + auto user = item->getUser(); + auto operand = item->get(); + if (!operand) + continue; - if (!getIROpInfo(operand->getOp()).isHoistable()) - continue; + if (!getIROpInfo(operand->getOp()).isHoistable()) + continue; - // We can't handle the case where operand and user are in different blocks. - if (operand->getParent() != user->getParent()) - continue; + // We can't handle the case where operand and user are in different blocks. + if (operand->getParent() != user->getParent()) + continue; - // We allow out-of-order uses in global scope. - if (operand->getParent() && operand->getParent()->getOp() == kIROp_Module) - continue; + // We allow out-of-order uses in global scope. + if (operand->getParent() && operand->getParent()->getOp() == kIROp_Module) + continue; - // If the operand is defined after user, move it to before user. - if (_isInstDefinedAfter(operand, user)) + // If the operand is defined after user, move it to before user. + if (_isInstDefinedAfter(operand, user)) + { + operand->insertBefore(user); + for (UInt i = 0; i < operand->getOperandCount(); i++) { - operand->insertBefore(user); - for (UInt i = 0; i < operand->getOperandCount(); i++) - { - workList2.add(operand->getOperands() + i); - } - workList2.add(&operand->typeUse); + workList2.add(operand->getOperands() + i); } + workList2.add(&operand->typeUse); } - workList1 = _Move(workList2); } + workList1 = _Move(workList2); } +} - static void _replaceInstUsesWith(IRInst* thisInst, IRInst* other) +static void _replaceInstUsesWith(IRInst* thisInst, IRInst* other) +{ + IRDeduplicationContext* dedupContext = nullptr; + + struct WorkItem { - IRDeduplicationContext* dedupContext = nullptr; + IRInst* thisInst; + IRInst* otherInst; + }; - struct WorkItem + // A work list of hoistable users for which we need + // to deduplicate/update their entry in the global numbering map. + List<WorkItem> workList; + HashSet<IRInst*> workListSet; + + auto addToWorkList = [&](IRInst* src, IRInst* target) + { + if (workListSet.add(src)) { - IRInst* thisInst; - IRInst* otherInst; - }; + WorkItem item; + item.thisInst = src; + item.otherInst = target; + workList.add(item); + } + }; + + addToWorkList(thisInst, other); + + for (Index i = 0; i < workList.getCount(); i++) + { + auto workItem = workList[i]; + thisInst = workItem.thisInst; + other = workItem.otherInst; + + SLANG_ASSERT(other); - // A work list of hoistable users for which we need - // to deduplicate/update their entry in the global numbering map. - List<WorkItem> workList; - HashSet<IRInst*> workListSet; + // Safety check: don't try to replace something with itself. + if (other == thisInst) + continue; - auto addToWorkList = [&](IRInst* src, IRInst* target) + if (getIROpInfo(thisInst->getOp()).isHoistable()) { - if (workListSet.add(src)) + if (!dedupContext) { - WorkItem item; - item.thisInst = src; - item.otherInst = target; - workList.add(item); + SLANG_ASSERT(thisInst->getModule()); + dedupContext = thisInst->getModule()->getDeduplicationContext(); } - }; + dedupContext->getInstReplacementMap()[thisInst] = other; + } - addToWorkList(thisInst, other); + // We will walk through the list of uses for the current + // instruction, and make them point to the other inst. + IRUse* ff = thisInst->firstUse; - for (Index i = 0; i < workList.getCount(); i++) - { - auto workItem = workList[i]; - thisInst = workItem.thisInst; - other = workItem.otherInst; + // No uses? Nothing to do. + if (!ff) + continue; - SLANG_ASSERT(other); + // ff->debugValidate(); - // Safety check: don't try to replace something with itself. - if (other == thisInst) - continue; + IRUse* uu = ff; + for (;;) + { + // The uses had better all be uses of this + // instruction, or invariants are broken. + SLANG_ASSERT(uu->get() == thisInst); - if (getIROpInfo(thisInst->getOp()).isHoistable()) + auto user = uu->getUser(); + bool userIsHoistable = getIROpInfo(user->getOp()).isHoistable(); + if (userIsHoistable) { if (!dedupContext) { - SLANG_ASSERT(thisInst->getModule()); - dedupContext = thisInst->getModule()->getDeduplicationContext(); + SLANG_ASSERT(user->getModule()); + dedupContext = user->getModule()->getDeduplicationContext(); } - dedupContext->getInstReplacementMap()[thisInst] = other; + dedupContext->_removeGlobalNumberingEntry(user); } - // We will walk through the list of uses for the current - // instruction, and make them point to the other inst. - IRUse* ff = thisInst->firstUse; - - // No uses? Nothing to do. - if (!ff) - continue; + // Swap this use over to use the other value. + uu->usedValue = other; - //ff->debugValidate(); + // If `other` is hoistable, then we need to make sure `other` is hoisted + // to a point before `user`, if it is not already so. + _maybeHoistOperand(uu); - IRUse* uu = ff; - for (;;) + if (userIsHoistable) { - // The uses had better all be uses of this - // instruction, or invariants are broken. - SLANG_ASSERT(uu->get() == thisInst); - - auto user = uu->getUser(); - bool userIsHoistable = getIROpInfo(user->getOp()).isHoistable(); - if (userIsHoistable) + // Is the updated inst already exists in the global numbering map? + // If so, we need to continue work on replacing the updated inst with the existing + // value. + IRInst* existingVal = nullptr; + if (dedupContext->getGlobalValueNumberingMap().tryGetValue( + IRInstKey{user}, + existingVal)) { - if (!dedupContext) - { - SLANG_ASSERT(user->getModule()); - dedupContext = user->getModule()->getDeduplicationContext(); - } - dedupContext->_removeGlobalNumberingEntry(user); + // If existingVal has been replaced by something else, use that. + dedupContext->getInstReplacementMap().tryGetValue(existingVal, existingVal); + addToWorkList(user, existingVal); } - - // Swap this use over to use the other value. - uu->usedValue = other; - - // If `other` is hoistable, then we need to make sure `other` is hoisted - // to a point before `user`, if it is not already so. - _maybeHoistOperand(uu); - - if (userIsHoistable) + else { - // Is the updated inst already exists in the global numbering map? - // If so, we need to continue work on replacing the updated inst with the existing value. - IRInst* existingVal = nullptr; - if (dedupContext->getGlobalValueNumberingMap().tryGetValue(IRInstKey{ user }, existingVal)) - { - // If existingVal has been replaced by something else, use that. - dedupContext->getInstReplacementMap().tryGetValue(existingVal, existingVal); - addToWorkList(user, existingVal); - } - else - { - dedupContext->_addGlobalNumberingEntry(user); - } + dedupContext->_addGlobalNumberingEntry(user); } - - // Try to move to the next use, but bail - // out if we are at the last one. - IRUse* nn = uu->nextUse; - if (!nn) - break; - - uu = nn; } - // We are at the last use (and there must - // be at least one, because we handled - // the case of an empty list earlier). - SLANG_ASSERT(uu); - - // Our job at this point is to splice - // our list of uses onto the other - // value's uses. - // - // If the value already had uses, then - // we need to patch our new list onto - // the front. - if (auto nn = other->firstUse) - { - uu->nextUse = nn; - nn->prevLink = &uu->nextUse; - } + // Try to move to the next use, but bail + // out if we are at the last one. + IRUse* nn = uu->nextUse; + if (!nn) + break; - // No matter what, our list of - // uses will become the start - // of the list of uses for - // `other` - other->firstUse = ff; - ff->prevLink = &other->firstUse; + uu = nn; + } - // And `this` will have no uses any more. - thisInst->firstUse = nullptr; + // We are at the last use (and there must + // be at least one, because we handled + // the case of an empty list earlier). + SLANG_ASSERT(uu); - ff->debugValidate(); + // Our job at this point is to splice + // our list of uses onto the other + // value's uses. + // + // If the value already had uses, then + // we need to patch our new list onto + // the front. + if (auto nn = other->firstUse) + { + uu->nextUse = nn; + nn->prevLink = &uu->nextUse; } - } + // No matter what, our list of + // uses will become the start + // of the list of uses for + // `other` + other->firstUse = ff; + ff->prevLink = &other->firstUse; - void IRInst::replaceUsesWith(IRInst* other) - { - _replaceInstUsesWith(this, other); - } + // And `this` will have no uses any more. + thisInst->firstUse = nullptr; - // Insert this instruction into the same basic block - // as `other`, right before it. - void IRInst::insertBefore(IRInst* other) - { - SLANG_ASSERT(other); - if (other->getPrevInst() == this) - return; - if (other == this) - return; - _insertAt(other->getPrevInst(), other, other->getParent()); + ff->debugValidate(); } +} - void IRInst::insertAtStart(IRInst* newParent) - { - SLANG_ASSERT(newParent); - _insertAt(nullptr, newParent->getFirstDecorationOrChild(), newParent); - } - - void IRInst::moveToStart() - { - auto p = parent; - removeFromParent(); - insertAtStart(p); - } +void IRInst::replaceUsesWith(IRInst* other) +{ + _replaceInstUsesWith(this, other); +} - void IRInst::_insertAt(IRInst* inPrev, IRInst* inNext, IRInst* inParent) - { - // Make sure this instruction has been removed from any previous parent - this->removeFromParent(); +// Insert this instruction into the same basic block +// as `other`, right before it. +void IRInst::insertBefore(IRInst* other) +{ + SLANG_ASSERT(other); + if (other->getPrevInst() == this) + return; + if (other == this) + return; + _insertAt(other->getPrevInst(), other, other->getParent()); +} - SLANG_ASSERT(inParent); - SLANG_ASSERT(!inPrev || (inPrev->getNextInst() == inNext) && (inPrev->getParent() == inParent)); - SLANG_ASSERT(!inNext || (inNext->getPrevInst() == inPrev) && (inNext->getParent() == inParent)); +void IRInst::insertAtStart(IRInst* newParent) +{ + SLANG_ASSERT(newParent); + _insertAt(nullptr, newParent->getFirstDecorationOrChild(), newParent); +} - if( inPrev ) - { - inPrev->next = this; - } - else - { - inParent->m_decorationsAndChildren.first = this; - } +void IRInst::moveToStart() +{ + auto p = parent; + removeFromParent(); + insertAtStart(p); +} - if (inNext) - { - inNext->prev = this; - } - else - { - inParent->m_decorationsAndChildren.last = this; - } +void IRInst::_insertAt(IRInst* inPrev, IRInst* inNext, IRInst* inParent) +{ + // Make sure this instruction has been removed from any previous parent + this->removeFromParent(); - this->prev = inPrev; - this->next = inNext; - this->parent = inParent; - -#if _DEBUG - validateIRInstOperands(this); -#endif - } + SLANG_ASSERT(inParent); + SLANG_ASSERT(!inPrev || (inPrev->getNextInst() == inNext) && (inPrev->getParent() == inParent)); + SLANG_ASSERT(!inNext || (inNext->getPrevInst() == inPrev) && (inNext->getParent() == inParent)); - void IRInst::insertAfter(IRInst* other) + if (inPrev) { - SLANG_ASSERT(other); - removeFromParent(); - _insertAt(other, other->getNextInst(), other->getParent()); + inPrev->next = this; } - - void IRInst::insertAtEnd(IRInst* newParent) + else { - SLANG_ASSERT(newParent); - removeFromParent(); - _insertAt(newParent->getLastDecorationOrChild(), nullptr, newParent); + inParent->m_decorationsAndChildren.first = this; } - void IRInst::moveToEnd() + if (inNext) { - auto p = parent; - removeFromParent(); - insertAtEnd(p); + inNext->prev = this; } - - void IRInst::insertAt(IRInsertLoc const& loc) + else { - removeFromParent(); - IRInst* other = loc.getInst(); - switch(loc.getMode()) - { - case IRInsertLoc::Mode::None: - break; - case IRInsertLoc::Mode::Before: - insertBefore(other); - break; - case IRInsertLoc::Mode::After: - insertAfter(other); - break; - case IRInsertLoc::Mode::AtStart: - insertAtStart(other); - break; - case IRInsertLoc::Mode::AtEnd: - insertAtEnd(other); - break; - } + inParent->m_decorationsAndChildren.last = this; } - // Remove this instruction from its parent block, - // and then destroy it (it had better have no uses!) - void IRInst::removeFromParent() - { - auto oldParent = getParent(); + this->prev = inPrev; + this->next = inNext; + this->parent = inParent; - // If we don't currently have a parent, then - // we are doing fine. - if(!oldParent) - return; +#if _DEBUG + validateIRInstOperands(this); +#endif +} - auto pp = getPrevInst(); - auto nn = getNextInst(); +void IRInst::insertAfter(IRInst* other) +{ + SLANG_ASSERT(other); + removeFromParent(); + _insertAt(other, other->getNextInst(), other->getParent()); +} - if(pp) - { - SLANG_ASSERT(pp->getParent() == oldParent); - pp->next = nn; - } - else - { - oldParent->m_decorationsAndChildren.first = nn; - } +void IRInst::insertAtEnd(IRInst* newParent) +{ + SLANG_ASSERT(newParent); + removeFromParent(); + _insertAt(newParent->getLastDecorationOrChild(), nullptr, newParent); +} - if(nn) - { - SLANG_ASSERT(nn->getParent() == oldParent); - nn->prev = pp; - } - else - { - oldParent->m_decorationsAndChildren.last = pp; - } +void IRInst::moveToEnd() +{ + auto p = parent; + removeFromParent(); + insertAtEnd(p); +} - prev = nullptr; - next = nullptr; - parent = nullptr; +void IRInst::insertAt(IRInsertLoc const& loc) +{ + removeFromParent(); + IRInst* other = loc.getInst(); + switch (loc.getMode()) + { + case IRInsertLoc::Mode::None: break; + case IRInsertLoc::Mode::Before: insertBefore(other); break; + case IRInsertLoc::Mode::After: insertAfter(other); break; + case IRInsertLoc::Mode::AtStart: insertAtStart(other); break; + case IRInsertLoc::Mode::AtEnd: insertAtEnd(other); break; } +} + +// Remove this instruction from its parent block, +// and then destroy it (it had better have no uses!) +void IRInst::removeFromParent() +{ + auto oldParent = getParent(); - void IRInst::removeArguments() + // If we don't currently have a parent, then + // we are doing fine. + if (!oldParent) + return; + + auto pp = getPrevInst(); + auto nn = getNextInst(); + + if (pp) { - typeUse.clear(); - for( UInt aa = 0; aa < operandCount; ++aa ) - { - IRUse& use = getOperands()[aa]; - use.clear(); - } + SLANG_ASSERT(pp->getParent() == oldParent); + pp->next = nn; } - - void IRInst::removeOperand(Index index) + else { - for (Index i = index; i < (Index)operandCount - 1; i++) - { - getOperands()[i].set(getOperand(i + 1)); - } - getOperands()[operandCount - 1].clear(); - operandCount--; - return; + oldParent->m_decorationsAndChildren.first = nn; } - // Remove this instruction from its parent block, - // and then destroy it (it had better have no uses!) - void IRInst::removeAndDeallocate() + if (nn) + { + SLANG_ASSERT(nn->getParent() == oldParent); + nn->prev = pp; + } + else { - removeAndDeallocateAllDecorationsAndChildren(); + oldParent->m_decorationsAndChildren.last = pp; + } - if (auto module = getModule()) - { - if (getIROpInfo(getOp()).isHoistable()) - { - module->getDeduplicationContext()->removeHoistableInstFromGlobalNumberingMap(this); - } - else if (auto constInst = as<IRConstant>(this)) - { - module->getDeduplicationContext()->getConstantMap().remove(IRConstantKey{ constInst }); - } - module->getDeduplicationContext()->getInstReplacementMap().remove(this); - if (auto func = as<IRGlobalValueWithCode>(this)) - module->invalidateAnalysisForInst(func); - } - removeArguments(); - removeFromParent(); + prev = nullptr; + next = nullptr; + parent = nullptr; +} - // Run destructor to be sure... - this->~IRInst(); +void IRInst::removeArguments() +{ + typeUse.clear(); + for (UInt aa = 0; aa < operandCount; ++aa) + { + IRUse& use = getOperands()[aa]; + use.clear(); } +} - void IRInst::removeAndDeallocateAllDecorationsAndChildren() +void IRInst::removeOperand(Index index) +{ + for (Index i = index; i < (Index)operandCount - 1; i++) { - IRInst* nextChild = nullptr; - for( IRInst* child = getFirstDecorationOrChild(); child; child = nextChild ) - { - nextChild = child->getNextInst(); - child->removeAndDeallocate(); - } + getOperands()[i].set(getOperand(i + 1)); } + getOperands()[operandCount - 1].clear(); + operandCount--; + return; +} - void IRInst::transferDecorationsTo(IRInst* target) +// Remove this instruction from its parent block, +// and then destroy it (it had better have no uses!) +void IRInst::removeAndDeallocate() +{ + removeAndDeallocateAllDecorationsAndChildren(); + + if (auto module = getModule()) { - while( auto decoration = getFirstDecoration() ) + if (getIROpInfo(getOp()).isHoistable()) + { + module->getDeduplicationContext()->removeHoistableInstFromGlobalNumberingMap(this); + } + else if (auto constInst = as<IRConstant>(this)) { - decoration->removeFromParent(); - decoration->insertAtStart(target); + module->getDeduplicationContext()->getConstantMap().remove(IRConstantKey{constInst}); } + module->getDeduplicationContext()->getInstReplacementMap().remove(this); + if (auto func = as<IRGlobalValueWithCode>(this)) + module->invalidateAnalysisForInst(func); } + removeArguments(); + removeFromParent(); - bool IRInst::mightHaveSideEffects(SideEffectAnalysisOptions options) - { - // TODO: We should drive this based on flags specified - // in `ir-inst-defs.h` isntead of hard-coding things here, - // but this is good enough for now if we are conservative: + // Run destructor to be sure... + this->~IRInst(); +} - if(as<IRType>(this)) - return false; +void IRInst::removeAndDeallocateAllDecorationsAndChildren() +{ + IRInst* nextChild = nullptr; + for (IRInst* child = getFirstDecorationOrChild(); child; child = nextChild) + { + nextChild = child->getNextInst(); + child->removeAndDeallocate(); + } +} - if(as<IRConstant>(this)) - return false; +void IRInst::transferDecorationsTo(IRInst* target) +{ + while (auto decoration = getFirstDecoration()) + { + decoration->removeFromParent(); + decoration->insertAtStart(target); + } +} - if(as<IRLayout>(this)) - return false; +bool IRInst::mightHaveSideEffects(SideEffectAnalysisOptions options) +{ + // TODO: We should drive this based on flags specified + // in `ir-inst-defs.h` isntead of hard-coding things here, + // but this is good enough for now if we are conservative: - if(as<IRAttr>(this)) - return false; + if (as<IRType>(this)) + return false; - if (as<IRSPIRVAsmOperand>(this)) - return false; + if (as<IRConstant>(this)) + return false; - switch(getOp()) - { - // By default, assume that we might have side effects, - // to safely cover all the instructions we haven't had time to think about. - default: - break; + if (as<IRLayout>(this)) + return false; - case kIROp_Call: - { - // In the general case, a function call must be assumed to - // have almost arbitrary side effects. - // - // However, it is possible that the callee can be identified, - // and it may be a function with an attribute that explicitly - // limits the side effects it is allowed to have. - // - // For now, we will explicitly check for the `[__readNone]` - // attribute, which was used to mark functions that compute - // their result strictly as a function of the arguments (and - // not anything they point to, or other non-argument state). - // Calls to such functions cannot have side effects (except - // for things like stack overflow that abstract language models - // tend to ignore), and can be subject to dead code elimination, - // common subexpression elimination, etc. - // - auto call = cast<IRCall>(this); - return !(isSideEffectFreeFunctionalCall(call, options)); - } - break; + if (as<IRAttr>(this)) + return false; - // All of the cases for "global values" are side-effect-free. - case kIROp_StructType: - case kIROp_StructField: - case kIROp_GLSLShaderStorageBufferType: - case kIROp_RTTIPointerType: - case kIROp_RTTIObject: - case kIROp_RTTIType: - case kIROp_Func: - case kIROp_Generic: - case kIROp_Var: - case kIROp_Param: - case kIROp_GlobalVar: // Note: the IRGlobalVar represents the *address*, so only a load/store would have side effects - case kIROp_GlobalConstant: - case kIROp_GlobalParam: - case kIROp_StructKey: - case kIROp_GlobalGenericParam: - case kIROp_ThisTypeWitness: - case kIROp_WitnessTable: - case kIROp_WitnessTableEntry: - case kIROp_InterfaceRequirementEntry: - case kIROp_Block: - case kIROp_Each: - case kIROp_TypeEqualityWitness: - return false; + if (as<IRSPIRVAsmOperand>(this)) + return false; - /// Liveness markers have no side effects - case kIROp_LiveRangeStart: - case kIROp_LiveRangeEnd: - - case kIROp_Nop: - case kIROp_undefined: - case kIROp_DefaultConstruct: - case kIROp_Specialize: - case kIROp_LookupWitness: - case kIROp_GetSequentialID: - case kIROp_GetAddr: - case kIROp_GetValueFromBoundInterface: - case kIROp_MakeUInt64: - case kIROp_MakeVector: - case kIROp_MakeMatrix: - case kIROp_MakeMatrixFromScalar: - case kIROp_MatrixReshape: - case kIROp_VectorReshape: - case kIROp_MakeWitnessPack: - case kIROp_MakeArray: - case kIROp_MakeArrayFromElement: - case kIROp_MakeStruct: - case kIROp_MakeString: - case kIROp_getNativeStr: - case kIROp_MakeResultError: - case kIROp_MakeResultValue: - case kIROp_GetResultError: - case kIROp_GetResultValue: - case kIROp_IsResultError: - case kIROp_MakeOptionalValue: - case kIROp_MakeOptionalNone: - case kIROp_OptionalHasValue: - case kIROp_GetOptionalValue: - case kIROp_DifferentialPairGetPrimal: - case kIROp_DifferentialPairGetDifferential: - case kIROp_MakeDifferentialPair: - case kIROp_MakeTuple: - case kIROp_MakeValuePack: - case kIROp_GetTupleElement: - case kIROp_StructuredBufferLoad: - case kIROp_RWStructuredBufferLoad: - case kIROp_RWStructuredBufferGetElementPtr: - case kIROp_CombinedTextureSamplerGetSampler: - case kIROp_CombinedTextureSamplerGetTexture: - case kIROp_Load: // We are ignoring the possibility of loads from bad addresses, or `volatile` loads - case kIROp_LoadReverseGradient: - case kIROp_ReverseGradientDiffPairRef: - case kIROp_ImageSubscript: - case kIROp_FieldExtract: - case kIROp_FieldAddress: - case kIROp_GetElement: - case kIROp_GetElementPtr: - case kIROp_GetOffsetPtr: - case kIROp_UpdateElement: - case kIROp_MeshOutputRef: - case kIROp_MakeVectorFromScalar: - case kIROp_swizzle: - case kIROp_swizzleSet: // Doesn't actually "set" anything - just returns the resulting vector - case kIROp_Add: - case kIROp_Sub: - case kIROp_Mul: - case kIROp_Lsh: - case kIROp_Rsh: - case kIROp_Eql: - case kIROp_Neq: - case kIROp_Greater: - case kIROp_Less: - case kIROp_Geq: - case kIROp_Leq: - case kIROp_BitAnd: - case kIROp_BitXor: - case kIROp_BitOr: - case kIROp_And: - case kIROp_Or: - case kIROp_Neg: - case kIROp_Not: - case kIROp_BitNot: - case kIROp_Select: - case kIROp_MakeExistential: - case kIROp_ExtractExistentialType: - case kIROp_ExtractExistentialValue: - case kIROp_ExtractExistentialWitnessTable: - case kIROp_WrapExistential: - case kIROp_BitCast: - case kIROp_CastFloatToInt: - case kIROp_CastIntToFloat: - case kIROp_IntCast: - case kIROp_FloatCast: - case kIROp_CastPtrToInt: - case kIROp_CastIntToPtr: - case kIROp_PtrCast: - case kIROp_CastDynamicResource: - case kIROp_AllocObj: - case kIROp_PackAnyValue: - case kIROp_UnpackAnyValue: - case kIROp_Reinterpret: - case kIROp_GetNativePtr: - case kIROp_BackwardDiffIntermediateContextType: - case kIROp_MakeTargetTuple: - case kIROp_GetTargetTupleElement: - case kIROp_TorchGetCudaStream: - case kIROp_MakeTensorView: - case kIROp_TorchTensorGetView: - case kIROp_GetStringHash: - case kIROp_AllocateOpaqueHandle: - case kIROp_GetArrayLength: - return false; + switch (getOp()) + { + // By default, assume that we might have side effects, + // to safely cover all the instructions we haven't had time to think about. + default: break; - case kIROp_ForwardDifferentiate: - case kIROp_BackwardDifferentiate: - case kIROp_BackwardDifferentiatePrimal: - case kIROp_BackwardDifferentiatePropagate: - case kIROp_DetachDerivative: - return false; + case kIROp_Call: + { + // In the general case, a function call must be assumed to + // have almost arbitrary side effects. + // + // However, it is possible that the callee can be identified, + // and it may be a function with an attribute that explicitly + // limits the side effects it is allowed to have. + // + // For now, we will explicitly check for the `[__readNone]` + // attribute, which was used to mark functions that compute + // their result strictly as a function of the arguments (and + // not anything they point to, or other non-argument state). + // Calls to such functions cannot have side effects (except + // for things like stack overflow that abstract language models + // tend to ignore), and can be subject to dead code elimination, + // common subexpression elimination, etc. + // + auto call = cast<IRCall>(this); + return !(isSideEffectFreeFunctionalCall(call, options)); + } + break; + + // All of the cases for "global values" are side-effect-free. + case kIROp_StructType: + case kIROp_StructField: + case kIROp_GLSLShaderStorageBufferType: + case kIROp_RTTIPointerType: + case kIROp_RTTIObject: + case kIROp_RTTIType: + case kIROp_Func: + case kIROp_Generic: + case kIROp_Var: + case kIROp_Param: + case kIROp_GlobalVar: // Note: the IRGlobalVar represents the *address*, so only a + // load/store would have side effects + case kIROp_GlobalConstant: + case kIROp_GlobalParam: + case kIROp_StructKey: + case kIROp_GlobalGenericParam: + case kIROp_ThisTypeWitness: + case kIROp_WitnessTable: + case kIROp_WitnessTableEntry: + case kIROp_InterfaceRequirementEntry: + case kIROp_Block: + case kIROp_Each: + case kIROp_TypeEqualityWitness: + return false; - case kIROp_Div: - case kIROp_IRem: - if (isIntegralScalarOrCompositeType(getFullType())) + /// Liveness markers have no side effects + case kIROp_LiveRangeStart: + case kIROp_LiveRangeEnd: + + case kIROp_Nop: + case kIROp_undefined: + case kIROp_DefaultConstruct: + case kIROp_Specialize: + case kIROp_LookupWitness: + case kIROp_GetSequentialID: + case kIROp_GetAddr: + case kIROp_GetValueFromBoundInterface: + case kIROp_MakeUInt64: + case kIROp_MakeVector: + case kIROp_MakeMatrix: + case kIROp_MakeMatrixFromScalar: + case kIROp_MatrixReshape: + case kIROp_VectorReshape: + case kIROp_MakeWitnessPack: + case kIROp_MakeArray: + case kIROp_MakeArrayFromElement: + case kIROp_MakeStruct: + case kIROp_MakeString: + case kIROp_getNativeStr: + case kIROp_MakeResultError: + case kIROp_MakeResultValue: + case kIROp_GetResultError: + case kIROp_GetResultValue: + case kIROp_IsResultError: + case kIROp_MakeOptionalValue: + case kIROp_MakeOptionalNone: + case kIROp_OptionalHasValue: + case kIROp_GetOptionalValue: + case kIROp_DifferentialPairGetPrimal: + case kIROp_DifferentialPairGetDifferential: + case kIROp_MakeDifferentialPair: + case kIROp_MakeTuple: + case kIROp_MakeValuePack: + case kIROp_GetTupleElement: + case kIROp_StructuredBufferLoad: + case kIROp_RWStructuredBufferLoad: + case kIROp_RWStructuredBufferGetElementPtr: + case kIROp_CombinedTextureSamplerGetSampler: + case kIROp_CombinedTextureSamplerGetTexture: + case kIROp_Load: // We are ignoring the possibility of loads from bad addresses, or + // `volatile` loads + case kIROp_LoadReverseGradient: + case kIROp_ReverseGradientDiffPairRef: + case kIROp_ImageSubscript: + case kIROp_FieldExtract: + case kIROp_FieldAddress: + case kIROp_GetElement: + case kIROp_GetElementPtr: + case kIROp_GetOffsetPtr: + case kIROp_UpdateElement: + case kIROp_MeshOutputRef: + case kIROp_MakeVectorFromScalar: + case kIROp_swizzle: + case kIROp_swizzleSet: // Doesn't actually "set" anything - just returns the resulting + // vector + case kIROp_Add: + case kIROp_Sub: + case kIROp_Mul: + case kIROp_Lsh: + case kIROp_Rsh: + case kIROp_Eql: + case kIROp_Neq: + case kIROp_Greater: + case kIROp_Less: + case kIROp_Geq: + case kIROp_Leq: + case kIROp_BitAnd: + case kIROp_BitXor: + case kIROp_BitOr: + case kIROp_And: + case kIROp_Or: + case kIROp_Neg: + case kIROp_Not: + case kIROp_BitNot: + case kIROp_Select: + case kIROp_MakeExistential: + case kIROp_ExtractExistentialType: + case kIROp_ExtractExistentialValue: + case kIROp_ExtractExistentialWitnessTable: + case kIROp_WrapExistential: + case kIROp_BitCast: + case kIROp_CastFloatToInt: + case kIROp_CastIntToFloat: + case kIROp_IntCast: + case kIROp_FloatCast: + case kIROp_CastPtrToInt: + case kIROp_CastIntToPtr: + case kIROp_PtrCast: + case kIROp_CastDynamicResource: + case kIROp_AllocObj: + case kIROp_PackAnyValue: + case kIROp_UnpackAnyValue: + case kIROp_Reinterpret: + case kIROp_GetNativePtr: + case kIROp_BackwardDiffIntermediateContextType: + case kIROp_MakeTargetTuple: + case kIROp_GetTargetTupleElement: + case kIROp_TorchGetCudaStream: + case kIROp_MakeTensorView: + case kIROp_TorchTensorGetView: + case kIROp_GetStringHash: + case kIROp_AllocateOpaqueHandle: + case kIROp_GetArrayLength: return false; + + case kIROp_ForwardDifferentiate: + case kIROp_BackwardDifferentiate: + case kIROp_BackwardDifferentiatePrimal: + case kIROp_BackwardDifferentiatePropagate: + case kIROp_DetachDerivative: return false; + + case kIROp_Div: + case kIROp_IRem: + if (isIntegralScalarOrCompositeType(getFullType())) + { + if (auto intLit = as<IRIntLit>(getOperand(1))) { - if (auto intLit = as<IRIntLit>(getOperand(1))) - { - if (intLit->getValue() != 0) - return false; - } - return true; + if (intLit->getValue() != 0) + return false; } - return false; - - case kIROp_FRem: - return false; + return true; } - return true; - } - - IRModule* IRInst::getModule() - { - IRInst* ii = this; - while(ii) - { - if(auto moduleInst = as<IRModuleInst>(ii)) - return moduleInst->module; + return false; - ii = ii->getParent(); - } - return nullptr; + case kIROp_FRem: return false; } + return true; +} - // - // IRType - // - - IRType* unwrapArray(IRType* type) +IRModule* IRInst::getModule() +{ + IRInst* ii = this; + while (ii) { - IRType* t = type; - while( auto arrayType = as<IRArrayTypeBase>(t) ) - { - t = arrayType->getElementType(); - } - return t; + if (auto moduleInst = as<IRModuleInst>(ii)) + return moduleInst->module; + + ii = ii->getParent(); } + return nullptr; +} - // - // IRTargetIntrinsicDecoration - // +// +// IRType +// - IRTargetIntrinsicDecoration* findAnyTargetIntrinsicDecoration( - IRInst* val) +IRType* unwrapArray(IRType* type) +{ + IRType* t = type; + while (auto arrayType = as<IRArrayTypeBase>(t)) { - IRInst* inst = getResolvedInstForDecorations(val); - return inst->findDecoration<IRTargetIntrinsicDecoration>(); + t = arrayType->getElementType(); } + return t; +} - template<typename T> - IRTargetSpecificDecoration* findBestTargetDecoration( - IRInst* inInst, - CapabilitySet const& targetCaps) - { - IRInst* inst = getResolvedInstForDecorations(inInst); - - // We will search through all the `IRTargetIntrinsicDecoration`s on - // the instruction, looking for those that are applicable to the - // current code generation target. Among the application decorations - // we will try to find one that is "best" in the sense that it is - // more (or at least as) specialized for the target than the - // others. - // - IRTargetSpecificDecoration* bestDecoration = nullptr; - CapabilitySet bestCaps; - for(auto dd : inst->getDecorations()) - { - auto decoration = as<IRTargetSpecificDecoration>(dd); - if(!decoration) - continue; - if (!T::isaImpl(decoration->getOp())) - continue; +// +// IRTargetIntrinsicDecoration +// - auto decorationCaps = decoration->getTargetCaps(); - if (decorationCaps.isIncompatibleWith(targetCaps)) +IRTargetIntrinsicDecoration* findAnyTargetIntrinsicDecoration(IRInst* val) +{ + IRInst* inst = getResolvedInstForDecorations(val); + return inst->findDecoration<IRTargetIntrinsicDecoration>(); +} + +template<typename T> +IRTargetSpecificDecoration* findBestTargetDecoration( + IRInst* inInst, + CapabilitySet const& targetCaps) +{ + IRInst* inst = getResolvedInstForDecorations(inInst); + + // We will search through all the `IRTargetIntrinsicDecoration`s on + // the instruction, looking for those that are applicable to the + // current code generation target. Among the application decorations + // we will try to find one that is "best" in the sense that it is + // more (or at least as) specialized for the target than the + // others. + // + IRTargetSpecificDecoration* bestDecoration = nullptr; + CapabilitySet bestCaps; + for (auto dd : inst->getDecorations()) + { + auto decoration = as<IRTargetSpecificDecoration>(dd); + if (!decoration) + continue; + if (!T::isaImpl(decoration->getOp())) + continue; + + auto decorationCaps = decoration->getTargetCaps(); + if (decorationCaps.isIncompatibleWith(targetCaps)) + continue; + + if (decoration->hasPredicate()) + { + const auto scrutinee = decoration->getTypeScrutinee(); + const auto predicate = decoration->getTypePredicate(); + const auto predicateFun = predicate == "boolean" ? [](auto t) + { return t->getOp() == kIROp_BoolType; } + : predicate == "integral" ? isIntegralType + : predicate == "floating" ? isFloatingType + : nullptr; + + SLANG_ASSERT(predicateFun); + if (!predicateFun(scrutinee)) continue; - - if(decoration->hasPredicate()) - { - const auto scrutinee = decoration->getTypeScrutinee(); - const auto predicate = decoration->getTypePredicate(); - const auto predicateFun = - predicate == "boolean" ? [](auto t){ return t->getOp() == kIROp_BoolType; } - : predicate == "integral" ? isIntegralType - : predicate == "floating" ? isFloatingType - : nullptr; - - SLANG_ASSERT(predicateFun); - if(!predicateFun(scrutinee)) - continue; - } - - bool isEqual; - if(!bestDecoration || decorationCaps.isBetterForTarget(bestCaps, targetCaps, isEqual)) - { - bestDecoration = decoration; - bestCaps = decorationCaps; - } } - return bestDecoration; + bool isEqual; + if (!bestDecoration || decorationCaps.isBetterForTarget(bestCaps, targetCaps, isEqual)) + { + bestDecoration = decoration; + bestCaps = decorationCaps; + } } - template<typename T> - IRTargetSpecificDecoration* findBestTargetDecoration( - IRInst* val, - CapabilityName targetCapabilityAtom) + return bestDecoration; +} + +template<typename T> +IRTargetSpecificDecoration* findBestTargetDecoration( + IRInst* val, + CapabilityName targetCapabilityAtom) +{ + return findBestTargetDecoration<T>(val, CapabilitySet(targetCapabilityAtom)); +} + +template IRTargetSpecificDecoration* findBestTargetDecoration<IRRequirePreludeDecoration>( + IRInst* val, + CapabilityName targetCapabilityAtom); + +bool findTargetIntrinsicDefinition( + IRInst* callee, + CapabilitySet const& targetCaps, + UnownedStringSlice& outDefinition, + IRInst*& outInst) +{ + if (auto decor = findBestTargetIntrinsicDecoration(callee, targetCaps)) { - return findBestTargetDecoration<T>(val, CapabilitySet(targetCapabilityAtom)); + outDefinition = decor->getDefinition(); + outInst = decor; + return true; } - - template - IRTargetSpecificDecoration* findBestTargetDecoration<IRRequirePreludeDecoration>( - IRInst* val, - CapabilityName targetCapabilityAtom); - - bool findTargetIntrinsicDefinition(IRInst* callee, CapabilitySet const& targetCaps, UnownedStringSlice& outDefinition, IRInst*& outInst) + auto func = as<IRGlobalValueWithCode>(callee); + if (!func) + return false; + for (auto block : func->getBlocks()) { - if (auto decor = findBestTargetIntrinsicDecoration(callee, targetCaps)) + if (auto genAsm = as<IRGenericAsm>(block->getTerminator())) { - outDefinition = decor->getDefinition(); - outInst = decor; + outDefinition = genAsm->getAsm(); + outInst = genAsm; return true; } - auto func = as<IRGlobalValueWithCode>(callee); - if (!func) - return false; - for (auto block : func->getBlocks()) - { - if (auto genAsm = as<IRGenericAsm>(block->getTerminator())) - { - outDefinition = genAsm->getAsm(); - outInst = genAsm; - return true; - } - } - return false; } + return false; +} #if 0 IRFunc* cloneSimpleFuncWithoutRegistering(IRSpecContextBase* context, IRFunc* originalFunc) @@ -8794,335 +8169,323 @@ namespace Slang } #endif - IRInst* findGenericReturnVal(IRGeneric* generic) - { - auto lastBlock = generic->getLastBlock(); - if (!lastBlock) - return nullptr; +IRInst* findGenericReturnVal(IRGeneric* generic) +{ + auto lastBlock = generic->getLastBlock(); + if (!lastBlock) + return nullptr; - auto returnInst = as<IRReturn>(lastBlock->getTerminator()); - if (!returnInst) - return nullptr; + auto returnInst = as<IRReturn>(lastBlock->getTerminator()); + if (!returnInst) + return nullptr; - auto val = returnInst->getVal(); - return val; - } - - IRInst* findInnerMostGenericReturnVal(IRGeneric* generic) + auto val = returnInst->getVal(); + return val; +} + +IRInst* findInnerMostGenericReturnVal(IRGeneric* generic) +{ + IRInst* inst = generic; + while (auto genericInst = as<IRGeneric>(inst)) + inst = findGenericReturnVal(genericInst); + return inst; +} + +IRInst* findOuterGeneric(IRInst* inst) +{ + if (inst) { - IRInst* inst = generic; - while (auto genericInst = as<IRGeneric>(inst)) - inst = findGenericReturnVal(genericInst); - return inst; + inst = inst->getParent(); } - - IRInst* findOuterGeneric(IRInst* inst) + else { - if (inst) - { - inst = inst->getParent(); - } - else - { - return nullptr; - } - - while(inst) - { - if (as<IRGeneric>(inst)) - return inst; - - inst = inst->getParent(); - } return nullptr; } - IRInst* maybeFindOuterGeneric(IRInst* inst) + while (inst) { - auto outerGeneric = findOuterGeneric(inst); - if (!outerGeneric) return inst; - return outerGeneric; - } + if (as<IRGeneric>(inst)) + return inst; - IRInst* findOuterMostGeneric(IRInst* inst) - { - IRInst* currInst = inst; - while(auto outerGeneric = findOuterGeneric(currInst)) - { - currInst = outerGeneric; - } - return currInst; + inst = inst->getParent(); } + return nullptr; +} + +IRInst* maybeFindOuterGeneric(IRInst* inst) +{ + auto outerGeneric = findOuterGeneric(inst); + if (!outerGeneric) + return inst; + return outerGeneric; +} - IRGeneric* findSpecializedGeneric(IRSpecialize* specialize) +IRInst* findOuterMostGeneric(IRInst* inst) +{ + IRInst* currInst = inst; + while (auto outerGeneric = findOuterGeneric(currInst)) { - return as<IRGeneric>(specialize->getBase()); + currInst = outerGeneric; } + return currInst; +} +IRGeneric* findSpecializedGeneric(IRSpecialize* specialize) +{ + return as<IRGeneric>(specialize->getBase()); +} - IRInst* findSpecializeReturnVal(IRSpecialize* specialize) - { - auto base = specialize->getBase(); - while( auto baseSpec = as<IRSpecialize>(base) ) - { - auto returnVal = findSpecializeReturnVal(baseSpec); - if(!returnVal) - break; +IRInst* findSpecializeReturnVal(IRSpecialize* specialize) +{ + auto base = specialize->getBase(); - base = returnVal; - } + while (auto baseSpec = as<IRSpecialize>(base)) + { + auto returnVal = findSpecializeReturnVal(baseSpec); + if (!returnVal) + break; - if( auto generic = as<IRGeneric>(base) ) - { - return findGenericReturnVal(generic); - } + base = returnVal; + } - return nullptr; + if (auto generic = as<IRGeneric>(base)) + { + return findGenericReturnVal(generic); } - IRInst* getResolvedInstForDecorations(IRInst* inst, bool resolveThroughDifferentiation) + return nullptr; +} + +IRInst* getResolvedInstForDecorations(IRInst* inst, bool resolveThroughDifferentiation) +{ + IRInst* candidate = inst; + for (;;) { - IRInst* candidate = inst; - for(;;) + if (auto specInst = as<IRSpecialize>(candidate)) { - if(auto specInst = as<IRSpecialize>(candidate)) + candidate = specInst->getBase(); + continue; + } + if (resolveThroughDifferentiation) + { + switch (candidate->getOp()) { - candidate = specInst->getBase(); + case kIROp_ForwardDifferentiate: + case kIROp_BackwardDifferentiate: + case kIROp_BackwardDifferentiatePrimal: + case kIROp_BackwardDifferentiatePropagate: + candidate = candidate->getOperand(0); continue; + default: break; } - if (resolveThroughDifferentiation) - { - switch (candidate->getOp()) - { - case kIROp_ForwardDifferentiate: - case kIROp_BackwardDifferentiate: - case kIROp_BackwardDifferentiatePrimal: - case kIROp_BackwardDifferentiatePropagate: - candidate = candidate->getOperand(0); - continue; - default: - break; - } - } - if( auto genericInst = as<IRGeneric>(candidate) ) + } + if (auto genericInst = as<IRGeneric>(candidate)) + { + if (auto returnVal = findGenericReturnVal(genericInst)) { - if( auto returnVal = findGenericReturnVal(genericInst) ) - { - candidate = returnVal; - continue; - } + candidate = returnVal; + continue; } - - return candidate; } + + return candidate; } +} - bool isDefinition( - IRInst* inVal) - { - IRInst* val = getResolvedInstForDecorations(inVal); +bool isDefinition(IRInst* inVal) +{ + IRInst* val = getResolvedInstForDecorations(inVal); - // Some cases of instructions have structural - // rules about when they are considered to have - // a definition (e.g., a function must have a body). - // - switch (val->getOp()) - { - case kIROp_Func: - return val->getFirstChild() != nullptr; + // Some cases of instructions have structural + // rules about when they are considered to have + // a definition (e.g., a function must have a body). + // + switch (val->getOp()) + { + case kIROp_Func: return val->getFirstChild() != nullptr; - case kIROp_GlobalConstant: - return cast<IRGlobalConstant>(val)->getValue() != nullptr; + case kIROp_GlobalConstant: return cast<IRGlobalConstant>(val)->getValue() != nullptr; - default: - break; - } - - // In all other cases, if we have an instruciton - // that has *not* been marked for import, then - // we consider it to be a definition. - return true; + default: break; } - void markConstExpr( - IRBuilder* builder, - IRInst* irValue) - { - // We will take an IR value with type `T`, - // and turn it into one with type `@ConstExpr T`. + // In all other cases, if we have an instruciton + // that has *not* been marked for import, then + // we consider it to be a definition. + return true; +} - // TODO: need to be careful if the value already has a rate - // qualifier set. +void markConstExpr(IRBuilder* builder, IRInst* irValue) +{ + // We will take an IR value with type `T`, + // and turn it into one with type `@ConstExpr T`. - irValue->setFullType( - builder->getRateQualifiedType( - builder->getConstExprRate(), - irValue->getDataType())); - } + // TODO: need to be careful if the value already has a rate + // qualifier set. - bool isBuiltin(IRInst* inst) - { - return inst->findDecoration<IRBuiltinDecoration>() != nullptr; - } - IRFunc* getParentFunc(IRInst* inst) - { - auto parent = inst->getParent(); - while (parent) - { - if (auto func = as<IRFunc>(parent)) - return func; - parent = parent->getParent(); - } - return nullptr; - } + irValue->setFullType( + builder->getRateQualifiedType(builder->getConstExprRate(), irValue->getDataType())); +} - bool hasDescendent(IRInst* inst, IRInst* child) +bool isBuiltin(IRInst* inst) +{ + return inst->findDecoration<IRBuiltinDecoration>() != nullptr; +} +IRFunc* getParentFunc(IRInst* inst) +{ + auto parent = inst->getParent(); + while (parent) { - auto parent = child->getParent(); - while (parent) - { - if (inst == parent) - return true; - parent = parent->getParent(); - } - return false; + if (auto func = as<IRFunc>(parent)) + return func; + parent = parent->getParent(); } + return nullptr; +} - IRInst* getGenericReturnVal(IRInst* inst) +bool hasDescendent(IRInst* inst, IRInst* child) +{ + auto parent = child->getParent(); + while (parent) { - if (auto gen = as<IRGeneric>(inst)) - { - return findGenericReturnVal(gen); - } - return inst; + if (inst == parent) + return true; + parent = parent->getParent(); } + return false; +} - IRDominatorTree* IRAnalysis::getDominatorTree() +IRInst* getGenericReturnVal(IRInst* inst) +{ + if (auto gen = as<IRGeneric>(inst)) { - return static_cast<IRDominatorTree*>(domTree.get()); + return findGenericReturnVal(gen); } + return inst; +} - bool isMovableInst(IRInst* inst) - { - // Don't try to modify hoistable insts, they are already globally deduplicated. - if (getIROpInfo(inst->getOp()).isHoistable()) - return false; +IRDominatorTree* IRAnalysis::getDominatorTree() +{ + return static_cast<IRDominatorTree*>(domTree.get()); +} - switch (inst->getOp()) - { - case kIROp_Add: - case kIROp_Sub: - case kIROp_Mul: - case kIROp_FRem: - case kIROp_IRem: - case kIROp_Lsh: - case kIROp_Rsh: - case kIROp_And: - case kIROp_Or: - case kIROp_Not: - case kIROp_Neg: - case kIROp_FieldExtract: - case kIROp_FieldAddress: - case kIROp_GetElement: - case kIROp_GetElementPtr: - case kIROp_GetOffsetPtr: - case kIROp_UpdateElement: - case kIROp_Specialize: - case kIROp_LookupWitness: - case kIROp_OptionalHasValue: - case kIROp_GetOptionalValue: - case kIROp_MakeOptionalValue: - case kIROp_MakeTuple: - case kIROp_GetTupleElement: - case kIROp_MakeStruct: - case kIROp_MakeArray: - case kIROp_MakeArrayFromElement: - case kIROp_MakeVector: - case kIROp_MakeMatrix: - case kIROp_MakeMatrixFromScalar: - case kIROp_MakeVectorFromScalar: - case kIROp_swizzle: - case kIROp_swizzleSet: - case kIROp_MatrixReshape: - case kIROp_MakeString: - case kIROp_MakeResultError: - case kIROp_MakeResultValue: - case kIROp_GetResultError: - case kIROp_GetResultValue: - case kIROp_CastFloatToInt: - case kIROp_CastIntToFloat: - case kIROp_CastIntToPtr: - case kIROp_CastPtrToBool: - case kIROp_CastPtrToInt: - case kIROp_PtrCast: - case kIROp_CastDynamicResource: - case kIROp_BitAnd: - case kIROp_BitNot: - case kIROp_BitOr: - case kIROp_BitXor: - case kIROp_BitCast: - case kIROp_IntCast: - case kIROp_FloatCast: - case kIROp_Reinterpret: - case kIROp_Greater: - case kIROp_Less: - case kIROp_Geq: - case kIROp_Leq: - case kIROp_Neq: - case kIROp_Eql: - case kIROp_ExtractExistentialType: - case kIROp_ExtractExistentialValue: - case kIROp_ExtractExistentialWitnessTable: - return true; - case kIROp_Call: - // Similar to the case in IRInst::mightHaveSideEffects, pure - // calls are ok - return isPureFunctionalCall(cast<IRCall>(inst)); - case kIROp_Load: - // Load is generally not movable, an exception is loading a global constant buffer. - if (auto load = as<IRLoad>(inst)) +bool isMovableInst(IRInst* inst) +{ + // Don't try to modify hoistable insts, they are already globally deduplicated. + if (getIROpInfo(inst->getOp()).isHoistable()) + return false; + + switch (inst->getOp()) + { + case kIROp_Add: + case kIROp_Sub: + case kIROp_Mul: + case kIROp_FRem: + case kIROp_IRem: + case kIROp_Lsh: + case kIROp_Rsh: + case kIROp_And: + case kIROp_Or: + case kIROp_Not: + case kIROp_Neg: + case kIROp_FieldExtract: + case kIROp_FieldAddress: + case kIROp_GetElement: + case kIROp_GetElementPtr: + case kIROp_GetOffsetPtr: + case kIROp_UpdateElement: + case kIROp_Specialize: + case kIROp_LookupWitness: + case kIROp_OptionalHasValue: + case kIROp_GetOptionalValue: + case kIROp_MakeOptionalValue: + case kIROp_MakeTuple: + case kIROp_GetTupleElement: + case kIROp_MakeStruct: + case kIROp_MakeArray: + case kIROp_MakeArrayFromElement: + case kIROp_MakeVector: + case kIROp_MakeMatrix: + case kIROp_MakeMatrixFromScalar: + case kIROp_MakeVectorFromScalar: + case kIROp_swizzle: + case kIROp_swizzleSet: + case kIROp_MatrixReshape: + case kIROp_MakeString: + case kIROp_MakeResultError: + case kIROp_MakeResultValue: + case kIROp_GetResultError: + case kIROp_GetResultValue: + case kIROp_CastFloatToInt: + case kIROp_CastIntToFloat: + case kIROp_CastIntToPtr: + case kIROp_CastPtrToBool: + case kIROp_CastPtrToInt: + case kIROp_PtrCast: + case kIROp_CastDynamicResource: + case kIROp_BitAnd: + case kIROp_BitNot: + case kIROp_BitOr: + case kIROp_BitXor: + case kIROp_BitCast: + case kIROp_IntCast: + case kIROp_FloatCast: + case kIROp_Reinterpret: + case kIROp_Greater: + case kIROp_Less: + case kIROp_Geq: + case kIROp_Leq: + case kIROp_Neq: + case kIROp_Eql: + case kIROp_ExtractExistentialType: + case kIROp_ExtractExistentialValue: + case kIROp_ExtractExistentialWitnessTable: return true; + case kIROp_Call: + // Similar to the case in IRInst::mightHaveSideEffects, pure + // calls are ok + return isPureFunctionalCall(cast<IRCall>(inst)); + case kIROp_Load: + // Load is generally not movable, an exception is loading a global constant buffer. + if (auto load = as<IRLoad>(inst)) + { + auto addrType = load->getPtr()->getDataType(); + switch (addrType->getOp()) { - auto addrType = load->getPtr()->getDataType(); - switch (addrType->getOp()) - { - case kIROp_ConstantBufferType: - case kIROp_ParameterBlockType: - return true; - default: - break; - } + case kIROp_ConstantBufferType: + case kIROp_ParameterBlockType: return true; + default: break; } - return false; - default: - return false; } + return false; + default: return false; } +} + +void IRInst::addBlock(IRBlock* block) +{ + block->insertAtEnd(this); +} - void IRInst::addBlock(IRBlock* block) +void IRInst::dump() +{ + if (auto intLit = as<IRIntLit>(this)) { - block->insertAtEnd(this); + std::cout << intLit->getValue() << std::endl; } - - void IRInst::dump() + else if (auto stringLit = as<IRStringLit>(this)) { - if (auto intLit = as<IRIntLit>(this)) - { - std::cout << intLit->getValue() << std::endl; - } - else if (auto stringLit = as<IRStringLit>(this)) - { - std::cout << stringLit->getStringSlice().begin() << std::endl; - } - else - { - StringBuilder sb; - IRDumpOptions options; - StringWriter writer(&sb, Slang::WriterFlag::AutoFlush); - dumpIR(this, options, nullptr, &writer); - std::cout << sb.toString().begin() << std::endl; - } + std::cout << stringLit->getStringSlice().begin() << std::endl; + } + else + { + StringBuilder sb; + IRDumpOptions options; + StringWriter writer(&sb, Slang::WriterFlag::AutoFlush); + dumpIR(this, options, nullptr, &writer); + std::cout << sb.toString().begin() << std::endl; } +} } // namespace Slang #if SLANG_VC |
