summaryrefslogtreecommitdiffstats
path: root/source/slang/ir.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-05-31 17:20:37 -0400
committerGitHub <noreply@github.com>2019-05-31 17:20:37 -0400
commit6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f (patch)
tree5a23cb47782e9e2a77762c90dd35da1005eba8d0 /source/slang/ir.cpp
parentb81ff3ef968d1cc4e954b31a1812b3c391d17b02 (diff)
Use slang- prefix on slang compiler and core source (#973)
* Prefixing source files in source/slang with slang- * Prefix source in source/slang with slang- prefix. * Rename core source files with slang- prefix. * Update project files. * Fix problems from automatic merge.
Diffstat (limited to 'source/slang/ir.cpp')
-rw-r--r--source/slang/ir.cpp4511
1 files changed, 0 insertions, 4511 deletions
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
deleted file mode 100644
index 1045b4884..000000000
--- a/source/slang/ir.cpp
+++ /dev/null
@@ -1,4511 +0,0 @@
-// ir.cpp
-#include "ir.h"
-#include "ir-insts.h"
-
-#include "../core/basic.h"
-
-#include "mangle.h"
-
-namespace Slang
-{
- struct IRSpecContext;
-
- IRInst* cloneGlobalValueWithLinkage(
- IRSpecContext* context,
- IRInst* originalVal,
- IRLinkageDecoration* originalLinkage);
-
- 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 "ir-inst-defs.h"
-
- // Pseudo ops
-#define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) /* empty */
-#define PSEUDO_INST(ID) \
- { kIRPseudoOp_##ID, { #ID, 0, 0 } },
-
- // First is 'invalid'
- { kIROp_Invalid,{ "invalid", 0, 0 } },
- // Then all the other psuedo ops
-#include "ir-inst-defs.h"
-
- };
-
- IROpInfo getIROpInfo(IROp opIn)
- {
- const int op = opIn & kIROpMeta_PseudoOpMask;
- if ((op & kIROpMeta_IsPseudoOp) && op < kIRPseudoOp_LastPlusOne)
- {
- // It's a pseudo op
- const int index = op - kIRPseudoOp_First;
- // Pseudo ops start from kIROpcount
- const auto& entry = kIROps[kIROpCount + index];
- SLANG_ASSERT(entry.op == op);
- return entry.info;
- }
- else 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;
- }
-
- IROp findIROp(const UnownedStringSlice& name)
- {
- for (auto ee : kIROps)
- {
- if (name == ee.info.name)
- return ee.op;
- }
-
- return IROp(kIROp_Invalid);
- }
-
-
-
- //
-
- void IRUse::debugValidate()
- {
-#ifdef _DEBUG
- 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);
-
- pp = &u->nextUse;
- u = u->nextUse;
- }
-#endif
- }
-
- void IRUse::init(IRInst* u, IRInst* v)
- {
- clear();
-
- user = u;
- usedValue = v;
- if(v)
- {
- nextUse = v->firstUse;
- prevLink = &v->firstUse;
-
- if(nextUse)
- {
- nextUse->prevLink = &this->nextUse;
- }
-
- v->firstUse = this;
- }
-
- debugValidate();
- }
-
- void IRUse::set(IRInst* uv)
- {
- init(user, uv);
- }
-
- void IRUse::clear()
- {
- // This `IRUse` is part of the linked list
- // of uses for `usedValue`.
-
- debugValidate();
-
- if (usedValue)
- {
- auto uv = usedValue;
-
- *prevLink = nextUse;
- if(nextUse)
- {
- nextUse->prevLink = prevLink;
- }
-
- user = nullptr;
- usedValue = nullptr;
- nextUse = nullptr;
- prevLink = nullptr;
-
- if(uv->firstUse)
- uv->firstUse->debugValidate();
- }
- }
-
- // IRInstListBase
-
- void IRInstListBase::Iterator::operator++()
- {
- if (inst)
- {
- inst = inst->next;
- }
- }
-
- 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.
-
- return (IRUse*)(this + 1);
- }
-
- IRDecoration* IRInst::findDecorationImpl(IROp decorationOp)
- {
- for(auto dd : getDecorations())
- {
- if(dd->op == decorationOp)
- return dd;
- }
- return nullptr;
- }
-
- // IRConstant
-
- IRIntegerValue GetIntVal(IRInst* inst)
- {
- switch (inst->op)
- {
- default:
- SLANG_UNEXPECTED("needed a known integer value");
- UNREACHABLE_RETURN(0);
-
- case kIROp_IntLit:
- return static_cast<IRConstant*>(inst)->value.intVal;
- break;
- }
- }
-
- // IRParam
-
- IRParam* IRParam::getNextParam()
- {
- return as<IRParam>(getNextInst());
- }
-
- // IRArrayTypeBase
-
- IRInst* IRArrayTypeBase::getElementCount()
- {
- if (auto arrayType = as<IRArrayType>(this))
- return arrayType->getElementCount();
-
- return nullptr;
- }
-
- // IRPtrTypeBase
-
- IRType* tryGetPointedToType(
- IRBuilder* builder,
- IRType* type)
- {
- if( auto rateQualType = as<IRRateQualifiedType>(type) )
- {
- type = rateQualType->getDataType();
- }
-
- // The "true" pointers and the pointer-like stdlib 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`.
- //
- // 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 )
- {
- existentialArgs.add(bindExistentials->getExistentialArg(ii));
- }
- return builder->getBindExistentialsType(
- baseElementType,
- existentialArgCount,
- existentialArgs.getBuffer());
- }
- }
-
- // TODO: We may need to handle other cases here.
-
- return nullptr;
- }
-
-
- // IRBlock
-
- IRParam* IRBlock::getLastParam()
- {
- IRParam* param = getFirstParam();
- if (!param) return nullptr;
-
- while (auto nextParam = param->getNextParam())
- param = nextParam;
-
- return param;
- }
-
- void IRBlock::addParam(IRParam* param)
- {
- // If there are any existing parameters,
- // then insert after the last of them.
- //
- if (auto lastParam = getLastParam())
- {
- param->insertAfter(lastParam);
- }
- //
- // 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.
- //
- else
- {
- param->insertAtEnd(this);
- }
- }
-
- IRInst* IRBlock::getFirstOrdinaryInst()
- {
- // 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();
- }
- }
-
- 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>(inst))
- return nullptr;
-
- // Otherwise the last instruction is the last "ordinary"
- // instruction as well.
- return inst;
- }
-
-
- // 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->op)
- {
- case kIROp_ReturnVal:
- case kIROp_ReturnVoid:
- case kIROp_Unreachable:
- case kIROp_MissingReturn:
- case kIROp_discard:
- 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;
-
- 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)
- {
- // 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;
- }
-
- 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)
- {
- (void)ii;
- count++;
- }
- return count;
- }
-
- bool IRBlock::PredecessorList::isEmpty()
- {
- return !(begin() != end());
- }
-
-
- void IRBlock::PredecessorList::Iterator::operator++()
- {
- if (!use) return;
- use = adjustPredecessorUse(use->nextUse);
- }
-
- IRBlock* IRBlock::PredecessorList::Iterator::operator*()
- {
- if (!use) return nullptr;
- return (IRBlock*)use->getUser()->parent;
- }
-
- 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)
-
- auto terminator = getLastInst();
- return Slang::getSuccessors(terminator);
- }
-
- UInt IRBlock::SuccessorList::getCount()
- {
- UInt count = 0;
- for (auto ii : *this)
- {
- (void)ii;
- count++;
- }
- return count;
- }
-
- void IRBlock::SuccessorList::Iterator::operator++()
- {
- use += stride;
- }
-
- IRBlock* IRBlock::SuccessorList::Iterator::operator*()
- {
- return (IRBlock*)use->get();
- }
-
- UInt IRUnconditionalBranch::getArgCount()
- {
- switch(op)
- {
- case kIROp_unconditionalBranch:
- return getOperandCount() - 1;
-
- case kIROp_loop:
- return getOperandCount() - 3;
-
- default:
- SLANG_UNEXPECTED("unhandled unconditional branch opcode");
- UNREACHABLE_RETURN(0);
- }
- }
-
- IRUse* IRUnconditionalBranch::getArgs()
- {
- switch(op)
- {
- case kIROp_unconditionalBranch:
- return getOperands() + 1;
-
- case kIROp_loop:
- return getOperands() + 3;
-
- default:
- SLANG_UNEXPECTED("unhandled unconditional branch opcode");
- UNREACHABLE_RETURN(0);
- }
- }
-
- IRInst* IRUnconditionalBranch::getArg(UInt index)
- {
- return getArgs()[index].usedValue;
- }
-
- IRParam* IRGlobalValueWithParams::getFirstParam()
- {
- auto entryBlock = getFirstBlock();
- if(!entryBlock) return nullptr;
-
- return entryBlock->getFirstParam();
- }
-
- IRParam* IRGlobalValueWithParams::getLastParam()
- {
- auto entryBlock = getFirstBlock();
- if(!entryBlock) return nullptr;
-
- return entryBlock->getLastParam();
- }
-
- IRInstList<IRParam> IRGlobalValueWithParams::getParams()
- {
- auto entryBlock = getFirstBlock();
- if(!entryBlock) return IRInstList<IRParam>();
-
- return entryBlock->getParams();
- }
-
-
- // IRFunc
-
- IRType* IRFunc::getResultType() { return getDataType()->getResultType(); }
- UInt IRFunc::getParamCount() { return getDataType()->getParamCount(); }
- IRType* IRFunc::getParamType(UInt index) { return getDataType()->getParamType(index); }
-
- void IRGlobalValueWithCode::addBlock(IRBlock* block)
- {
- block->insertAtEnd(this);
- }
-
- void fixUpFuncType(IRFunc* func)
- {
- SLANG_ASSERT(func);
-
- auto irModule = func->getModule();
- SLANG_ASSERT(irModule);
-
- SharedIRBuilder sharedBuilder;
- sharedBuilder.module = irModule;
-
- IRBuilder builder;
- builder.sharedBuilder = &sharedBuilder;
-
- builder.setInsertBefore(func);
-
- List<IRType*> paramTypes;
- for(auto param : func->getParams())
- {
- paramTypes.add(param->getFullType());
- }
-
- auto resultType = func->getResultType();
-
- auto funcType = builder.getFuncType(paramTypes, resultType);
- builder.setDataType(func, funcType);
- }
-
- //
-
- bool isTerminatorInst(IROp op)
- {
- switch (op)
- {
- default:
- return false;
-
- case kIROp_ReturnVal:
- case kIROp_ReturnVoid:
- case kIROp_unconditionalBranch:
- case kIROp_conditionalBranch:
- case kIROp_loop:
- case kIROp_ifElse:
- case kIROp_discard:
- case kIROp_Switch:
- case kIROp_Unreachable:
- case kIROp_MissingReturn:
- return true;
- }
- }
-
- bool isTerminatorInst(IRInst* inst)
- {
- if (!inst) return false;
- return isTerminatorInst(inst->op);
- }
-
- //
-
- IRBlock* IRBuilder::getBlock()
- {
- return as<IRBlock>(insertIntoParent);
- }
-
- // Get the current function (or other value with code)
- // that we are inserting into (if any).
- IRGlobalValueWithCode* IRBuilder::getFunc()
- {
- auto pp = insertIntoParent;
- if (auto block = as<IRBlock>(pp))
- {
- pp = pp->getParent();
- }
- return as<IRGlobalValueWithCode>(pp);
- }
-
-
- void IRBuilder::setInsertInto(IRInst* insertInto)
- {
- insertIntoParent = insertInto;
- insertBeforeInst = nullptr;
- }
-
- void IRBuilder::setInsertBefore(IRInst* insertBefore)
- {
- SLANG_ASSERT(insertBefore);
- insertIntoParent = insertBefore->parent;
- insertBeforeInst = insertBefore;
- }
-
-
- // Add an instruction into the current scope
- void IRBuilder::addInst(
- IRInst* inst)
- {
- if(insertBeforeInst)
- {
- inst->insertBefore(insertBeforeInst);
- }
- else if (insertIntoParent)
- {
- inst->insertAtEnd(insertIntoParent);
- }
- else
- {
- // Don't append the instruction anywhere
- }
- }
-
- // 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);
- //
- // 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()))
- {
- // 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).
- //
- return leftBlock;
- }
-
- //
- // 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.
- //
- if (!leftNonBlock) return right;
- if (!rightNonBlock) return left;
-
- // 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;
- }
- }
-
- // 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);
-
- // As a fallback, try to use the left parent as a default
- // in case things go badly.
- //
- if (!parentNonBlock)
- {
- parentNonBlock = leftNonBlock;
- }
-
- IRInst* parent = 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.
-
- // 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 = leftBlock;
- }
- else if (rightBlock && (parentNonBlock == rightNonBlock))
- {
- // We have a right block, and have picked its parent.
-
- // We already tested above, so we know there isn't a
- // matching situation on the left side.
-
- parent = rightBlock;
- }
-
- // 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();
- }
-
- return parent;
- }
-
- IRInst* createEmptyInst(
- IRModule* module,
- IROp op,
- int totalArgCount)
- {
- size_t size = sizeof(IRInst) + (totalArgCount) * sizeof(IRUse);
-
- SLANG_ASSERT(module);
- IRInst* inst = (IRInst*)module->memoryArena.allocateAndZero(size);
-
- inst->operandCount = uint32_t(totalArgCount);
- inst->op = op;
-
- return inst;
- }
-
- IRInst* createEmptyInstWithSize(
- IRModule* module,
- IROp op,
- size_t totalSizeInBytes)
- {
- SLANG_ASSERT(totalSizeInBytes >= sizeof(IRInst));
-
- SLANG_ASSERT(module);
- IRInst* inst = (IRInst*)module->memoryArena.allocateAndZero(totalSizeInBytes);
-
- inst->operandCount = 0;
- inst->op = op;
-
- return inst;
- }
-
- // 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();
-
- // 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;
-
- auto operandParent = operand->getParent();
-
- parent = mergeCandidateParentsForHoistableInst(parent, operandParent);
- }
-
- // We better have ended up with a place to insert.
- SLANG_ASSERT(parent);
-
- // If we have chosen to insert into the same parent that the
- // IRBuilder is configured to use, then respect its `insertBeforeInst`
- // setting.
- if (parent == builder->insertIntoParent)
- {
- builder->addInst(inst);
- return;
- }
-
- // Otherwise, we just want to insert at the end of the chosen parent.
- //
- // TODO: be careful about inserting after the terminator of a block...
-
- inst->insertAtEnd(parent);
- }
-
- static void maybeSetSourceLoc(
- IRBuilder* builder,
- IRInst* value)
- {
- if(!builder)
- return;
-
- auto sourceLocInfo = builder->sourceLocInfo;
- if(!sourceLocInfo)
- return;
-
- // Try to find something with usable location info
- for(;;)
- {
- if(sourceLocInfo->sourceLoc.getRaw())
- break;
-
- if(!sourceLocInfo->next)
- break;
-
- sourceLocInfo = sourceLocInfo->next;
- }
-
- value->sourceLoc = sourceLocInfo->sourceLoc;
- }
-
- // 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(
- IRModule* module,
- IRBuilder* builder,
- IROp op,
- IRType* type,
- UInt fixedArgCount,
- IRInst* const* fixedArgs,
- UInt varArgListCount,
- UInt const* listArgCounts,
- IRInst* const* const* listArgs)
- {
- UInt varArgCount = 0;
- for (UInt ii = 0; ii < varArgListCount; ++ii)
- {
- varArgCount += listArgCounts[ii];
- }
-
- UInt size = sizeof(IRInst) + (fixedArgCount + varArgCount) * sizeof(IRUse);
- if (sizeof(T) > size)
- {
- size = sizeof(T);
- }
-
- SLANG_ASSERT(module);
- T* inst = (T*)module->memoryArena.allocateAndZero(size);
-
- // TODO: Do we need to run ctor after zeroing?
- new(inst)T();
-
- inst->operandCount = (uint32_t)(fixedArgCount + varArgCount);
-
- inst->op = op;
-
- if (type)
- {
- inst->typeUse.init(inst, type);
- }
-
- maybeSetSourceLoc(builder, inst);
-
- auto operand = inst->getOperands();
-
- for( UInt aa = 0; aa < fixedArgCount; ++aa )
- {
- if (fixedArgs)
- {
- operand->init(inst, fixedArgs[aa]);
- }
- operand++;
- }
-
- for (UInt ii = 0; ii < varArgListCount; ++ii)
- {
- UInt listArgCount = listArgCounts[ii];
- for (UInt jj = 0; jj < listArgCount; ++jj)
- {
- if (listArgs[ii])
- {
- operand->init(inst, listArgs[ii][jj]);
- }
- else
- {
- operand->init(inst, nullptr);
- }
- operand++;
- }
- }
- return inst;
- }
-
- static IRInst* createInstWithSizeImpl(
- IRBuilder* builder,
- IROp op,
- IRType* type,
- size_t sizeInBytes)
- {
- auto module = builder->getModule();
- IRInst* inst = (IRInst*)module->memoryArena.allocate(sizeInBytes);
- // Zero only the 'type'
- memset(inst, 0, sizeof(IRInst));
- // TODO: Do we need to run ctor after zeroing?
- new (inst) IRInst;
-
- inst->op = op;
- if (type)
- {
- inst->typeUse.init(inst, type);
- }
- maybeSetSourceLoc(builder, inst);
- return inst;
- }
-
- template<typename T>
- static T* createInstImpl(
- IRBuilder* builder,
- IROp op,
- IRType* type,
- UInt fixedArgCount,
- IRInst* const* fixedArgs,
- UInt varArgCount = 0,
- IRInst* const* varArgs = nullptr)
- {
- return createInstImpl<T>(
- builder->getModule(),
- builder,
- op,
- type,
- fixedArgCount,
- fixedArgs,
- 1,
- &varArgCount,
- &varArgs);
- }
-
- template<typename T>
- static T* createInstImpl(
- IRBuilder* builder,
- IROp op,
- IRType* type,
- UInt fixedArgCount,
- IRInst* const* fixedArgs,
- UInt varArgListCount,
- UInt const* listArgCount,
- IRInst* const* const* listArgs)
- {
- return createInstImpl<T>(
- builder->getModule(),
- builder,
- op,
- type,
- fixedArgCount,
- fixedArgs,
- varArgListCount,
- listArgCount,
- listArgs);
- }
-
- template<typename T>
- static T* createInst(
- IRBuilder* builder,
- IROp op,
- IRType* type,
- UInt argCount,
- IRInst* const* args)
- {
- return createInstImpl<T>(
- builder,
- op,
- type,
- argCount,
- args);
- }
-
- 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* 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* createInstWithTrailingArgs(
- IRBuilder* builder,
- IROp op,
- IRType* type,
- UInt argCount,
- IRInst* const* args)
- {
- return createInstImpl<T>(
- builder,
- op,
- type,
- argCount,
- args);
- }
-
- template<typename T>
- static T* createInstWithTrailingArgs(
- IRBuilder* builder,
- IROp op,
- IRType* type,
- UInt fixedArgCount,
- IRInst* const* fixedArgs,
- UInt 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,
- UInt varArgCount,
- IRInst* const* varArgs)
- {
- IRInst* fixedArgs[] = { arg1 };
- UInt fixedArgCount = sizeof(fixedArgs) / sizeof(fixedArgs[0]);
-
- return createInstImpl<T>(
- builder,
- op,
- type,
- fixedArgCount,
- fixedArgs,
- varArgCount,
- varArgs);
- }
- //
-
- bool operator==(IRInstKey const& left, IRInstKey const& right)
- {
- if(left.inst->op != right.inst->op) return false;
- if(left.inst->getFullType() != right.inst->getFullType()) return false;
- if(left.inst->operandCount != right.inst->operandCount) return false;
-
- auto argCount = left.inst->operandCount;
- auto leftArgs = left.inst->getOperands();
- auto rightArgs = right.inst->getOperands();
- for( UInt aa = 0; aa < argCount; ++aa )
- {
- if(leftArgs[aa].get() != rightArgs[aa].get())
- return false;
- }
-
- return true;
- }
-
- int IRInstKey::GetHashCode()
- {
- auto code = Slang::GetHashCode(inst->op);
- 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 )
- {
- code = combineHash(code, Slang::GetHashCode(args[aa].get()));
- }
- return code;
- }
-
- UnownedStringSlice IRConstant::getStringSlice()
- {
- assert(op == kIROp_StringLit);
- // 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 UnownedStringSlice(value.transitoryStringVal.chars, value.transitoryStringVal.numChars);
- }
- else
- {
- return UnownedStringSlice(value.stringVal.chars, value.stringVal.numChars);
- }
- }
-
- bool IRConstant::isValueEqual(IRConstant* rhs)
- {
- // If they are literally the same thing..
- if (this == rhs)
- {
- return true;
- }
- // Check the type and they are the same op & same type
- if (op != rhs->op)
- {
- return false;
- }
-
- switch (op)
- {
- 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_StringLit:
- {
- return getStringSlice() == rhs->getStringSlice();
- }
- default: break;
- }
-
- SLANG_ASSERT(!"Unhandled type");
- return false;
- }
-
- /// 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();
- }
-
- int IRConstant::getHashCode()
- {
- auto code = Slang::GetHashCode(op);
- code = combineHash(code, Slang::GetHashCode(getFullType()));
-
- switch (op)
- {
- 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_StringLit:
- {
- const UnownedStringSlice slice = getStringSlice();
- return combineHash(code, Slang::GetHashCode(slice.begin(), slice.size()));
- }
- default:
- {
- SLANG_ASSERT(!"Invalid type");
- return 0;
- }
- }
- }
-
- static IRConstant* findOrEmitConstant(
- IRBuilder* builder,
- 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( builder->sharedBuilder->constantMap.TryGetValue(key, irValue) )
- {
- // We found a match, so just use that.
- return irValue;
- }
-
- // Calculate the minimum object size (ie not including the payload of value)
- const size_t prefixSize = SLANG_OFFSET_OF(IRConstant, value);
-
- switch (keyInst.op)
- {
- default:
- SLANG_UNEXPECTED("missing case for IR constant");
- break;
-
- case kIROp_BoolLit:
- case kIROp_IntLit:
- {
- irValue = static_cast<IRConstant*>(createInstWithSizeImpl(builder, keyInst.op, keyInst.getFullType(), prefixSize + sizeof(IRIntegerValue)));
- irValue->value.intVal = keyInst.value.intVal;
- break;
- }
- case kIROp_FloatLit:
- {
- irValue = static_cast<IRConstant*>(createInstWithSizeImpl(builder, keyInst.op, keyInst.getFullType(), prefixSize + sizeof(IRFloatingPointValue)));
- irValue->value.floatVal = keyInst.value.floatVal;
- break;
- }
- case kIROp_PtrLit:
- {
- irValue = static_cast<IRConstant*>(createInstWithSizeImpl(builder, keyInst.op, keyInst.getFullType(), prefixSize + sizeof(void*)));
- irValue->value.ptrVal = keyInst.value.ptrVal;
- break;
- }
- case kIROp_StringLit:
- {
- const UnownedStringSlice slice = keyInst.getStringSlice();
-
- const size_t sliceSize = slice.size();
- const size_t instSize = prefixSize + offsetof(IRConstant::StringValue, chars) + sliceSize;
-
- irValue = static_cast<IRConstant*>(createInstWithSizeImpl(builder, keyInst.op, keyInst.getFullType(), instSize));
-
- 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);
-
- break;
- }
- }
-
- key.inst = irValue;
- builder->sharedBuilder->constantMap.Add(key, irValue);
-
- addHoistableInst(builder, irValue);
-
- return irValue;
- }
-
- //
-
- IRInst* IRBuilder::getBoolValue(bool inValue)
- {
- IRConstant keyInst;
- memset(&keyInst, 0, sizeof(keyInst));
- keyInst.op = kIROp_BoolLit;
- keyInst.typeUse.usedValue = getBoolType();
- keyInst.value.intVal = IRIntegerValue(inValue);
- return findOrEmitConstant(this, keyInst);
- }
-
- IRInst* IRBuilder::getIntValue(IRType* type, IRIntegerValue inValue)
- {
- IRConstant keyInst;
- memset(&keyInst, 0, sizeof(keyInst));
- keyInst.op = kIROp_IntLit;
- keyInst.typeUse.usedValue = type;
- keyInst.value.intVal = inValue;
- return findOrEmitConstant(this, keyInst);
- }
-
- IRInst* IRBuilder::getFloatValue(IRType* type, IRFloatingPointValue inValue)
- {
- IRConstant keyInst;
- memset(&keyInst, 0, sizeof(keyInst));
- keyInst.op = kIROp_FloatLit;
- keyInst.typeUse.usedValue = type;
- keyInst.value.floatVal = inValue;
- return findOrEmitConstant(this, 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.op = kIROp_TransitoryDecoration;
- stackDecoration.insertAtEnd(&keyInst);
-
- keyInst.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.size());
-
- return static_cast<IRStringLit*>(findOrEmitConstant(this, keyInst));
- }
-
- IRPtrLit* IRBuilder::getPtrValue(void* value)
- {
- IRType* type = getPtrType(getVoidType());
-
- IRConstant keyInst;
- memset(&keyInst, 0, sizeof(keyInst));
- keyInst.op = kIROp_PtrLit;
- keyInst.typeUse.usedValue = type;
- keyInst.value.ptrVal = value;
- return (IRPtrLit*) findOrEmitConstant(this, keyInst);
- }
-
-
- IRInst* findOrEmitHoistableInst(
- IRBuilder* builder,
- IRType* type,
- IROp op,
- UInt operandListCount,
- UInt const* listOperandCounts,
- IRInst* const* const* listOperands)
- {
- UInt operandCount = 0;
- for (UInt ii = 0; ii < operandListCount; ++ii)
- {
- operandCount += listOperandCounts[ii];
- }
-
- auto& memoryArena = builder->getModule()->memoryArena;
- 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);
-
- void* endCursor = memoryArena.getCursor();
- // Mark as 'unused' cos it is unused on release builds.
- SLANG_UNUSED(endCursor);
-
- new(inst) IRInst();
- inst->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)
- {
- IRUse* operand = inst->getOperands();
- for (UInt ii = 0; ii < operandListCount; ++ii)
- {
- UInt listOperandCount = listOperandCounts[ii];
- for (UInt jj = 0; jj < listOperandCount; ++jj)
- {
- operand->usedValue = listOperands[ii][jj];
- operand++;
- }
- }
- }
-
- // Find or add the key/inst
- {
- IRInstKey key = { inst };
-
- // Ideally we would add if not found, else return if was found instead of testing & then adding.
- IRInst** found = builder->sharedBuilder->globalValueNumberingMap.TryGetValueOrAdd(key, inst);
- SLANG_ASSERT(endCursor == memoryArena.getCursor());
- // If it's found, just return, and throw away the instruction
- if (found)
- {
- memoryArena.rewindToCursor(cursor);
- return *found;
- }
- }
-
- // Make the lookup 'inst' instruction into 'proper' instruction. Equivalent to
- // IRInst* inst = createInstImpl<IRInst>(builder, op, type, 0, nullptr, operandListCount, listOperandCounts, listOperands);
- {
- if (type)
- {
- inst->typeUse.usedValue = nullptr;
- inst->typeUse.init(inst, type);
- }
-
- maybeSetSourceLoc(builder, 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);
- }
- }
-
- addHoistableInst(builder, inst);
-
- return inst;
- }
-
- IRInst* findOrEmitHoistableInst(
- IRBuilder* builder,
- IRType* type,
- IROp op,
- UInt operandCount,
- IRInst* const* operands)
- {
- return findOrEmitHoistableInst(
- builder,
- type,
- op,
- 1,
- &operandCount,
- &operands);
- }
-
- IRInst* findOrEmitHoistableInst(
- IRBuilder* builder,
- IRType* type,
- IROp op,
- IRInst* operand,
- UInt operandCount,
- IRInst* const* operands)
- {
- UInt counts[] = { 1, operandCount };
- IRInst* const* lists[] = { &operand, operands };
-
- return findOrEmitHoistableInst(
- builder,
- type,
- op,
- 2,
- counts,
- lists);
- }
-
-
- IRType* IRBuilder::getType(
- IROp op,
- UInt operandCount,
- IRInst* const* operands)
- {
- return (IRType*) findOrEmitHoistableInst(
- this,
- nullptr,
- op,
- operandCount,
- operands);
- }
-
- IRType* IRBuilder::getType(
- IROp op)
- {
- return getType(op, 0, nullptr);
- }
-
- IRBasicType* IRBuilder::getBasicType(BaseType baseType)
- {
- return (IRBasicType*)getType(
- IROp((UInt)kIROp_FirstBasicType + (UInt)baseType));
- }
-
- IRBasicType* IRBuilder::getVoidType()
- {
- return (IRVoidType*)getType(kIROp_VoidType);
- }
-
- IRBasicType* IRBuilder::getBoolType()
- {
- return (IRBoolType*)getType(kIROp_BoolType);
- }
-
- IRBasicType* IRBuilder::getIntType()
- {
- return (IRBasicType*)getType(kIROp_IntType);
- }
-
- IRStringType* IRBuilder::getStringType()
- {
- return (IRStringType*)getType(kIROp_StringType);
- }
-
- IRBasicBlockType* IRBuilder::getBasicBlockType()
- {
- return (IRBasicBlockType*)getType(kIROp_BasicBlockType);
- }
-
- IRTypeKind* IRBuilder::getTypeKind()
- {
- return (IRTypeKind*)getType(kIROp_TypeKind);
- }
-
- IRGenericKind* IRBuilder::getGenericKind()
- {
- return (IRGenericKind*)getType(kIROp_GenericKind);
- }
-
- IRPtrType* IRBuilder::getPtrType(IRType* valueType)
- {
- return (IRPtrType*) getPtrType(kIROp_PtrType, valueType);
- }
-
- IROutType* IRBuilder::getOutType(IRType* valueType)
- {
- return (IROutType*) getPtrType(kIROp_OutType, valueType);
- }
-
- IRInOutType* IRBuilder::getInOutType(IRType* valueType)
- {
- return (IRInOutType*) getPtrType(kIROp_InOutType, valueType);
- }
-
- IRRefType* IRBuilder::getRefType(IRType* valueType)
- {
- return (IRRefType*) getPtrType(kIROp_RefType, valueType);
- }
-
- IRPtrTypeBase* IRBuilder::getPtrType(IROp op, IRType* valueType)
- {
- IRInst* operands[] = { valueType };
- return (IRPtrTypeBase*) getType(
- op,
- 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);
- }
-
- IRUnsizedArrayType* IRBuilder::getUnsizedArrayType(
- IRType* elementType)
- {
- IRInst* operands[] = { elementType };
- 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);
- }
-
- IRMatrixType* IRBuilder::getMatrixType(
- IRType* elementType,
- IRInst* rowCount,
- IRInst* columnCount)
- {
- IRInst* operands[] = { elementType, rowCount, columnCount };
- return (IRMatrixType*)getType(
- kIROp_MatrixType,
- sizeof(operands) / sizeof(operands[0]),
- operands);
- }
-
- IRFuncType* IRBuilder::getFuncType(
- UInt paramCount,
- IRType* const* paramTypes,
- IRType* resultType)
- {
- return (IRFuncType*) findOrEmitHoistableInst(
- this,
- nullptr,
- kIROp_FuncType,
- resultType,
- paramCount,
- (IRInst* const*) paramTypes);
- }
-
- IRConstantBufferType* IRBuilder::getConstantBufferType(IRType* elementType)
- {
- IRInst* operands[] = { elementType };
- return (IRConstantBufferType*) getType(
- kIROp_ConstantBufferType,
- 1,
- operands);
- }
-
- IRConstExprRate* IRBuilder::getConstExprRate()
- {
- return (IRConstExprRate*)getType(kIROp_ConstExprRate);
- }
-
- IRGroupSharedRate* IRBuilder::getGroupSharedRate()
- {
- return (IRGroupSharedRate*)getType(kIROp_GroupSharedRate);
- }
-
- IRRateQualifiedType* IRBuilder::getRateQualifiedType(
- IRRate* rate,
- IRType* dataType)
- {
- IRInst* operands[] = { rate, dataType };
- return (IRRateQualifiedType*)getType(
- kIROp_RateQualifiedType,
- sizeof(operands) / sizeof(operands[0]),
- operands);
- }
-
- IRType* IRBuilder::getTaggedUnionType(
- UInt caseCount,
- IRType* const* caseTypes)
- {
- return (IRType*) findOrEmitHoistableInst(
- this,
- getTypeKind(),
- kIROp_TaggedUnionType,
- caseCount,
- (IRInst* const*) caseTypes);
- }
-
- IRType* IRBuilder::getBindExistentialsType(
- IRInst* baseType,
- UInt slotArgCount,
- IRInst* const* slotArgs)
- {
- if(slotArgCount == 0)
- return (IRType*) baseType;
-
- // 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 >= 1)
- {
- // We are being asked to emit `BindExistentials(someInterface, someConcreteType, ...)`
- // so we just want to return `ExistentialBox<someConcreteType>`.
- //
- auto concreteType = (IRType*) slotArgs[0];
- auto ptrType = getPtrType(kIROp_ExistentialBoxType, concreteType);
- return ptrType;
- }
- }
-
- return (IRType*) findOrEmitHoistableInst(
- this,
- getTypeKind(),
- kIROp_BindExistentialsType,
- baseType,
- slotArgCount,
- (IRInst* const*) slotArgs);
- }
-
- IRType* IRBuilder::getBindExistentialsType(
- IRInst* baseType,
- UInt slotArgCount,
- IRUse const* slotArgUses)
- {
- if(slotArgCount == 0)
- return (IRType*) baseType;
-
- List<IRInst*> slotArgs;
- for( UInt ii = 0; ii < slotArgCount; ++ii )
- {
- slotArgs.add(slotArgUses[ii].get());
- }
- return getBindExistentialsType(
- baseType,
- slotArgCount,
- slotArgs.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);
-
- inst->setFullType(newRateQualifiedType);
- }
- else
- {
- // No rate? Just clobber the data type.
- inst->setFullType(dataType);
- }
- }
-
-
- IRUndefined* IRBuilder::emitUndefined(IRType* type)
- {
- auto inst = createInst<IRUndefined>(
- this,
- kIROp_undefined,
- type);
-
- 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;
- }
-
- IRInst* IRBuilder::emitExtractExistentialWitnessTable(
- IRInst* existentialValue)
- {
- auto type = getWitnessTableType();
- auto inst = createInst<IRInst>(
- this,
- kIROp_ExtractExistentialWitnessTable,
- type,
- 1,
- &existentialValue);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitSpecializeInst(
- IRType* type,
- IRInst* genericVal,
- UInt argCount,
- IRInst* const* args)
- {
- auto inst = createInstWithTrailingArgs<IRSpecialize>(
- this,
- kIROp_Specialize,
- type,
- 1,
- &genericVal,
- argCount,
- args);
-
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitLookupInterfaceMethodInst(
- IRType* type,
- IRInst* witnessTableVal,
- IRInst* interfaceMethodVal)
- {
- auto inst = createInst<IRLookupWitnessMethod>(
- this,
- kIROp_lookup_interface_method,
- type,
- witnessTableVal,
- interfaceMethodVal);
-
- addInst(inst);
- return inst;
- }
-
- IRInst* 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::createIntrinsicInst(
- IRType* type,
- IROp op,
- UInt argCount,
- IRInst* const* args)
- {
- return createInstWithTrailingArgs<IRInst>(
- this,
- op,
- type,
- argCount,
- args);
- }
-
-
- IRInst* IRBuilder::emitIntrinsicInst(
- IRType* type,
- IROp op,
- UInt argCount,
- IRInst* const* args)
- {
- auto inst = createIntrinsicInst(
- type,
- op,
- argCount,
- args);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitConstructorInst(
- IRType* type,
- UInt argCount,
- IRInst* const* args)
- {
- auto inst = createInstWithTrailingArgs<IRInst>(
- this,
- kIROp_Construct,
- type,
- argCount,
- args);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitMakeVector(
- IRType* type,
- UInt argCount,
- IRInst* const* args)
- {
- return emitIntrinsicInst(type, kIROp_makeVector, argCount, args);
- }
-
- 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::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::emitWrapExistential(
- IRType* type,
- IRInst* value,
- UInt slotArgCount,
- IRInst* const* slotArgs)
- {
- 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)
- {
- // 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 : ExistentialBox<T>` for some `T`.
- //
- // We want to emit `makeExistential(load(value), witnessTable)`.
- //
- auto deref = emitLoad(value);
- return emitMakeExistential(type, deref, slotArgs[1]);
- }
- }
-
- IRInst* fixedArgs[] = {value};
- auto inst = createInstImpl<IRInst>(
- this,
- kIROp_WrapExistential,
- type,
- SLANG_COUNT_OF(fixedArgs),
- fixedArgs,
- slotArgCount,
- slotArgs);
- addInst(inst);
- return inst;
- }
-
- IRModule* IRBuilder::createModule()
- {
- auto module = new IRModule();
- module->session = getSession();
-
- auto moduleInst = createInstImpl<IRModuleInst>(
- module,
- this,
- kIROp_Module,
- nullptr,
- 0,
- nullptr,
- 0,
- nullptr,
- nullptr);
- module->moduleInst = moduleInst;
- moduleInst->module = module;
-
- return module;
- }
-
- 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 parent = builder->insertIntoParent;
- 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;
- }
-
- // Otherwise, move up the chain.
- parent = parent->parent;
- }
-
- // 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)
- {
- parent = builder->getModule()->getModuleInst();
- }
-
- // 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 == builder->insertIntoParent
- && builder->insertBeforeInst)
- {
- value->insertBefore(builder->insertBeforeInst);
- }
- else
- {
- value->insertAtEnd(parent);
- }
- }
-
- IRFunc* IRBuilder::createFunc()
- {
- IRFunc* rsFunc = createInst<IRFunc>(
- this,
- kIROp_Func,
- nullptr);
- maybeSetSourceLoc(this, rsFunc);
- addGlobalValue(this, rsFunc);
- return rsFunc;
- }
-
- IRGlobalVar* IRBuilder::createGlobalVar(
- IRType* valueType)
- {
- auto ptrType = getPtrType(valueType);
- IRGlobalVar* globalVar = createInst<IRGlobalVar>(
- this,
- kIROp_GlobalVar,
- ptrType);
- maybeSetSourceLoc(this, globalVar);
- addGlobalValue(this, globalVar);
- return globalVar;
- }
-
- IRGlobalConstant* IRBuilder::createGlobalConstant(
- IRType* valueType)
- {
- IRGlobalConstant* globalConstant = createInst<IRGlobalConstant>(
- this,
- kIROp_GlobalConstant,
- valueType);
- maybeSetSourceLoc(this, globalConstant);
- addGlobalValue(this, globalConstant);
- return globalConstant;
- }
-
- IRGlobalParam* IRBuilder::createGlobalParam(
- IRType* valueType)
- {
- IRGlobalParam* inst = createInst<IRGlobalParam>(
- this,
- kIROp_GlobalParam,
- valueType);
- maybeSetSourceLoc(this, inst);
- addGlobalValue(this, inst);
- return inst;
- }
-
- IRWitnessTable* IRBuilder::createWitnessTable()
- {
- IRWitnessTable* witnessTable = createInst<IRWitnessTable>(
- this,
- kIROp_WitnessTable,
- nullptr);
- addGlobalValue(this, witnessTable);
- return witnessTable;
- }
-
- IRWitnessTableEntry* IRBuilder::createWitnessTableEntry(
- IRWitnessTable* witnessTable,
- IRInst* requirementKey,
- IRInst* satisfyingVal)
- {
- IRWitnessTableEntry* entry = createInst<IRWitnessTableEntry>(
- this,
- kIROp_WitnessTableEntry,
- nullptr,
- requirementKey,
- satisfyingVal);
-
- if (witnessTable)
- {
- entry->insertAtEnd(witnessTable);
- }
-
- return entry;
- }
-
- IRStructType* IRBuilder::createStructType()
- {
- IRStructType* structType = createInst<IRStructType>(
- this,
- kIROp_StructType,
- nullptr);
- addGlobalValue(this, structType);
- return structType;
- }
-
- IRInterfaceType* IRBuilder::createInterfaceType()
- {
- IRInterfaceType* interfaceType = createInst<IRInterfaceType>(
- this,
- kIROp_InterfaceType,
- nullptr);
- 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(
- IRStructType* structType,
- IRStructKey* fieldKey,
- IRType* fieldType)
- {
- IRInst* operands[] = { fieldKey, fieldType };
- IRStructField* field = (IRStructField*) createInstWithTrailingArgs<IRInst>(
- this,
- kIROp_StructField,
- nullptr,
- 0,
- nullptr,
- 2,
- operands);
-
- if (structType)
- {
- field->insertAtEnd(structType);
- }
-
- return field;
- }
-
- IRGeneric* IRBuilder::createGeneric()
- {
- IRGeneric* irGeneric = createInst<IRGeneric>(
- this,
- kIROp_Generic,
- nullptr);
- return irGeneric;
- }
-
- IRGeneric* IRBuilder::emitGeneric()
- {
- auto irGeneric = createGeneric();
- addGlobalValue(this, irGeneric);
- return irGeneric;
- }
-
- IRBlock* IRBuilder::createBlock()
- {
- return createInst<IRBlock>(
- this,
- kIROp_Block,
- getBasicBlockType());
- }
-
- 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);
- }
- }
-
- IRBlock* IRBuilder::emitBlock()
- {
- auto block = createBlock();
- insertBlock(block);
- return block;
- }
-
- IRParam* IRBuilder::createParam(
- IRType* type)
- {
- auto param = createInst<IRParam>(
- this,
- kIROp_Param,
- type);
- return param;
- }
-
- IRParam* IRBuilder::emitParam(
- IRType* type)
- {
- auto param = createParam(type);
- if (auto bb = getBlock())
- {
- bb->addParam(param);
- }
- return param;
- }
-
- IRVar* IRBuilder::emitVar(
- IRType* type)
- {
- auto allocatedType = getPtrType(type);
- auto inst = createInst<IRVar>(
- this,
- kIROp_Var,
- allocatedType);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitLoad(
- IRType* type,
- IRInst* ptr)
- {
- auto inst = createInst<IRLoad>(
- this,
- kIROp_Load,
- type,
- ptr);
-
- addInst(inst);
- return inst;
- }
-
- 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))
- {
- valueType = rateType->getValueType();
- }
-
- return emitLoad(valueType, ptr);
- }
-
- IRInst* IRBuilder::emitStore(
- IRInst* dstPtr,
- IRInst* srcVal)
- {
- auto inst = createInst<IRStore>(
- this,
- kIROp_Store,
- nullptr,
- dstPtr,
- srcVal);
-
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitFieldExtract(
- IRType* type,
- IRInst* base,
- IRInst* field)
- {
- auto inst = createInst<IRFieldExtract>(
- this,
- kIROp_FieldExtract,
- type,
- base,
- field);
-
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitFieldAddress(
- IRType* type,
- IRInst* base,
- IRInst* field)
- {
- auto inst = createInst<IRFieldAddress>(
- this,
- kIROp_FieldAddress,
- type,
- base,
- field);
-
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitElementExtract(
- IRType* type,
- IRInst* base,
- IRInst* index)
- {
- auto inst = createInst<IRFieldAddress>(
- this,
- kIROp_getElement,
- type,
- base,
- index);
-
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitElementAddress(
- IRType* type,
- IRInst* basePtr,
- IRInst* index)
- {
- auto inst = createInst<IRFieldAddress>(
- this,
- kIROp_getElementPtr,
- type,
- basePtr,
- index);
-
- addInst(inst);
- return inst;
- }
-
- 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::emitSwizzle(
- IRType* type,
- IRInst* base,
- UInt elementCount,
- UInt const* elementIndices)
- {
- auto intType = getBasicType(BaseType::Int);
-
- IRInst* irElementIndices[4];
- for (UInt ii = 0; ii < elementCount; ++ii)
- {
- irElementIndices[ii] = getIntValue(intType, elementIndices[ii]);
- }
-
- return emitSwizzle(type, base, elementCount, irElementIndices);
- }
-
-
- 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* irElementIndices[4];
- for (UInt ii = 0; ii < elementCount; ++ii)
- {
- irElementIndices[ii] = getIntValue(intType, elementIndices[ii]);
- }
-
- return emitSwizzleSet(type, base, source, elementCount, irElementIndices);
- }
-
- 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* irElementIndices[4];
- for (UInt ii = 0; ii < elementCount; ++ii)
- {
- irElementIndices[ii] = getIntValue(intType, elementIndices[ii]);
- }
-
- return emitSwizzledStore(dest, source, elementCount, irElementIndices);
- }
-
- IRInst* IRBuilder::emitReturn(
- IRInst* val)
- {
- auto inst = createInst<IRReturnVal>(
- this,
- kIROp_ReturnVal,
- nullptr,
- val);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitReturn()
- {
- auto inst = createInst<IRReturnVoid>(
- this,
- kIROp_ReturnVoid,
- nullptr);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitUnreachable()
- {
- auto inst = createInst<IRUnreachable>(
- this,
- kIROp_Unreachable,
- nullptr);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitMissingReturn()
- {
- auto inst = createInst<IRMissingReturn>(
- this,
- kIROp_MissingReturn,
- nullptr);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitDiscard()
- {
- auto inst = createInst<IRDiscard>(
- this,
- kIROp_discard,
- nullptr);
- addInst(inst);
- return inst;
- }
-
-
- IRInst* IRBuilder::emitBranch(
- IRBlock* pBlock)
- {
- auto inst = createInst<IRUnconditionalBranch>(
- this,
- kIROp_unconditionalBranch,
- nullptr,
- pBlock);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitBreak(
- IRBlock* target)
- {
- return emitBranch(target);
- }
-
- IRInst* IRBuilder::emitContinue(
- IRBlock* target)
- {
- return emitBranch(target);
- }
-
- 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::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;
- }
-
- IRInst* 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::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::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()
- {
- IRGlobalGenericParam* irGenericParam = createInst<IRGlobalGenericParam>(
- this,
- kIROp_GlobalGenericParam,
- nullptr);
- addGlobalValue(this, irGenericParam);
- return irGenericParam;
- }
-
- IRBindGlobalGenericParam* IRBuilder::emitBindGlobalGenericParam(
- IRInst* param,
- IRInst* val)
- {
- auto inst = createInst<IRBindGlobalGenericParam>(
- this,
- kIROp_BindGlobalGenericParam,
- nullptr,
- param,
- val);
- addInst(inst);
- return inst;
- }
-
- IRInst* IRBuilder::emitBindGlobalExistentialSlots(
- UInt argCount,
- IRInst* const* args)
- {
- auto inst = createInstWithTrailingArgs<IRInst>(
- this,
- kIROp_BindGlobalExistentialSlots,
- getVoidType(),
- 0,
- nullptr,
- argCount,
- args);
- 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);
-
- decoration->insertAtStart(value);
-
- return decoration;
- }
-
- IRInst* IRBuilder::emitExtractTaggedUnionTag(
- IRInst* val)
- {
- auto inst = createInst<IRInst>(
- this,
- kIROp_ExtractTaggedUnionTag,
- getBasicType(BaseType::UInt),
- val);
- 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::emitBitCast(
- IRType* type,
- IRInst* val)
- {
- auto inst = createInst<IRInst>(
- this,
- kIROp_BitCast,
- type,
- val);
- addInst(inst);
- return inst;
- }
-
- //
- // Decorations
- //
-
- IRDecoration* IRBuilder::addDecoration(IRInst* value, IROp op, IRInst* const* operands, Int operandCount)
- {
- 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 decoration;
- }
-
-
- void IRBuilder::addHighLevelDeclDecoration(IRInst* inst, Decl* decl)
- {
- auto ptrConst = getPtrValue(addRefObjectToFree(decl));
- addDecoration(inst, kIROp_HighLevelDeclDecoration, ptrConst);
- }
-
- void IRBuilder::addLayoutDecoration(IRInst* inst, Layout* layout)
- {
- auto ptrConst = getPtrValue(addRefObjectToFree(layout));
- addDecoration(inst, kIROp_LayoutDecoration, ptrConst);
- }
-
- //
-
-
- struct IRDumpContext
- {
- StringBuilder* builder = nullptr;
- int indent = 0;
- IRDumpMode mode = IRDumpMode::Simplified;
-
- Dictionary<IRInst*, String> mapValueToName;
- Dictionary<String, UInt> uniqueNameCounters;
- UInt uniqueIDCounter = 1;
- };
-
- static void dump(
- IRDumpContext* context,
- char const* text)
- {
- context->builder->append(text);
- }
-
- static void dump(
- IRDumpContext* context,
- String const& text)
- {
- context->builder->append(text);
- }
-
- /*
- static void dump(
- IRDumpContext* context,
- UInt val)
- {
- context->builder->append(val);
- }
- */
-
- static void dump(
- IRDumpContext* context,
- IntegerLiteralValue val)
- {
- context->builder->append(val);
- }
-
- static void dump(
- IRDumpContext* context,
- FloatingPointLiteralValue val)
- {
- context->builder->append(val);
- }
-
- static void dumpIndent(
- IRDumpContext* context)
- {
- for (int ii = 0; ii < context->indent; ++ii)
- {
- dump(context, "\t");
- }
- }
-
- 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->op == 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 nam
- // Special case a name that is the empty string, just in case.
- if(name.getLength() == 0)
- {
- sb.append('_');
- }
-
- int prevChar = -1;
- for(auto c : name)
- {
- 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
- {
- // 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;
- }
-
- // 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'))
- {
- sb.append('_');
- }
- }
-
- static String createName(
- IRDumpContext* context,
- IRInst* value)
- {
- if(auto nameHintDecoration = value->findDecoration<IRNameHintDecoration>())
- {
- String nameHint = nameHintDecoration->getName();
-
- StringBuilder sb;
- scrubName(nameHint, sb);
-
- String key = sb.ProduceString();
- UInt count = 0;
- context->uniqueNameCounters.TryGetValue(key, count);
-
- context->uniqueNameCounters[key] = count+1;
-
- if(count)
- {
- sb.append(count);
- }
- return sb.ProduceString();
- }
- else
- {
- StringBuilder sb;
- auto id = context->uniqueIDCounter++;
- sb.append(id);
- return sb.ProduceString();
- }
- }
-
- static String getName(
- IRDumpContext* context,
- IRInst* value)
- {
- String name;
- if (context->mapValueToName.TryGetValue(value, name))
- return name;
-
- name = createName(context, value);
- context->mapValueToName.Add(value, name);
- return name;
- }
-
- static void dumpID(
- IRDumpContext* context,
- IRInst* inst)
- {
- if (!inst)
- {
- dump(context, "<null>");
- return;
- }
-
- if( opHasResult(inst) || instHasUses(inst) )
- {
- dump(context, "%");
- dump(context, getName(context, inst));
- }
- else
- {
- dump(context, "_");
- }
- }
-
-
-
- struct StringEncoder
- {
- static char getHexChar(int v)
- {
- return (v <= 9) ? char(v + '0') : char(v - 10 + 'A');
- }
-
- void flush(const char* pos)
- {
- if (pos > m_runStart)
- {
- m_builder->append(m_runStart, pos);
- }
- m_runStart = pos + 1;
- }
-
- void appendEscapedChar(const char* pos, char encodeChar)
- {
- flush(pos);
- const char chars[] = { '\\', encodeChar };
- m_builder->Append(chars, 2);
- }
-
- void appendAsHex(const char* pos)
- {
- flush(pos);
-
- const int v = *(const uint8_t*)pos;
-
- char buf[5];
- buf[0] = '\\';
- buf[1] = 'x';
- buf[2] = '0';
-
- buf[3] = getHexChar(v >> 4);
- buf[4] = getHexChar(v & 0xf);
-
- m_builder->Append(buf, 5);
- }
-
- StringEncoder(StringBuilder* builder, const char* start):
- m_runStart(start),
- m_builder(builder)
- {}
-
- StringBuilder* m_builder;
- const char* m_runStart;
- };
-
- static void dumpEncodeString(
- IRDumpContext* context,
- const UnownedStringSlice& slice)
- {
- // https://msdn.microsoft.com/en-us/library/69ze775t.aspx
-
- StringBuilder& builder = *context->builder;
- builder.Append('"');
-
- {
- const char* cur = slice.begin();
- StringEncoder encoder(&builder, cur);
- const char* end = slice.end();
-
- for (; cur < end; cur++)
- {
- const int8_t c = uint8_t(*cur);
- switch (c)
- {
- case '\\':
- encoder.appendEscapedChar(cur, '\\');
- break;
- case '"':
- encoder.appendEscapedChar(cur, '"');
- break;
- case '\n':
- encoder.appendEscapedChar(cur, 'n');
- break;
- case '\t':
- encoder.appendEscapedChar(cur, 't');
- break;
- case '\r':
- encoder.appendEscapedChar(cur, 'r');
- break;
- case '\0':
- encoder.appendEscapedChar(cur, '0');
- break;
- default:
- {
- if (c < 32)
- {
- encoder.appendAsHex(cur);
- }
- break;
- }
- }
- }
- encoder.flush(end);
- }
-
- builder.Append('"');
- }
-
- 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->mode == IRDumpMode::Detailed)
- return false;
-
- if(as<IRConstant>(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->op )
- {
- case kIROp_StructType:
- case kIROp_InterfaceType:
- return false;
-
- default:
- break;
- }
-
- if(as<IRType>(inst))
- return true;
-
- return false;
- }
-
- static void dumpInst(
- IRDumpContext* context,
- IRInst* inst);
-
- 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;
- }
-
- if(shouldFoldInstIntoUses(context, inst))
- {
- dumpInstExpr(context, inst);
- return;
- }
-
- dumpID(context, inst);
- }
-
- static void dumpType(
- IRDumpContext* context,
- IRType* type)
- {
- 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);
- }
-
- static void dumpInstTypeClause(
- IRDumpContext* context,
- IRType* type)
- {
- dump(context, "\t: ");
- dumpType(context, type);
-
- }
-
- void dumpIRDecorations(
- IRDumpContext* context,
- IRInst* inst)
- {
- for(auto dd : inst->getDecorations())
- {
- // Certain decorations aren't helpful to appear
- // in output dumps, so we will only include them
- // in the "detailed" dumping mode.
- //
- // For all other modes, we will check the opcode
- // and skip selected decorations.
- //
- if(context->mode != IRDumpMode::Detailed)
- {
- switch(dd->op)
- {
- default:
- break;
-
- case kIROp_HighLevelDeclDecoration:
- case kIROp_LayoutDecoration:
- continue;
- }
- }
-
- dump(context, "[");
- dumpInstBody(context, dd);
- dump(context, "]\n");
-
- dumpIndent(context);
- }
- }
-
- static void dumpBlock(
- IRDumpContext* context,
- IRBlock* block)
- {
- context->indent--;
- dump(context, "block ");
- dumpID(context, block);
-
- IRInst* inst = block->getFirstInst();
-
- // First walk through any `param` instructions,
- // so that we can format them nicely
- if (auto firstParam = as<IRParam>(inst))
- {
- dump(context, "(\n");
- context->indent += 2;
-
- for(;;)
- {
- auto param = as<IRParam>(inst);
- if (!param)
- break;
-
- if (param != firstParam)
- dump(context, ",\n");
-
- inst = inst->getNextInst();
-
- 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++;
-
- for(; inst; inst = inst->getNextInst())
- {
- dumpInst(context, inst);
- }
- }
-
- void dumpIRGlobalValueWithCode(
- IRDumpContext* context,
- IRGlobalValueWithCode* code)
- {
- auto opInfo = getIROpInfo(code->op);
-
- dumpIndent(context);
- dump(context, opInfo.name);
- dump(context, " ");
- dumpID(context, code);
-
- dumpInstTypeClause(context, code->getFullType());
-
- if (!code->getFirstBlock())
- {
- // Just a declaration.
- dump(context, ";\n");
- return;
- }
-
- dump(context, "\n");
-
- dumpIndent(context);
- dump(context, "{\n");
- context->indent++;
-
- for (auto bb = code->getFirstBlock(); bb; bb = bb->getNextBlock())
- {
- if (bb != code->getFirstBlock())
- dump(context, "\n");
- dumpBlock(context, bb);
- }
-
- context->indent--;
- dump(context, "}");
- }
-
-
- 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->op);
-
- dumpIndent(context);
- dump(context, opInfo.name);
- dump(context, " ");
- dumpID(context, inst);
-
- dumpInstTypeClause(context, inst->getFullType());
-
- if (!inst->getFirstChild())
- {
- // Empty.
- dump(context, ";\n");
- return;
- }
-
- dump(context, "\n");
-
- dumpIndent(context);
- dump(context, "{\n");
- context->indent++;
-
- for(auto child : inst->getChildren())
- {
- dumpInst(context, child);
- }
-
- context->indent--;
- 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())
- {
- dumpInst(context, ii);
- }
-
- context->indent--;
- dump(context, "}\n");
- }
-
- static void dumpInstExpr(
- IRDumpContext* context,
- IRInst* inst)
- {
- if (!inst)
- {
- dump(context, "<null>");
- return;
- }
-
- auto op = inst->op;
- auto opInfo = getIROpInfo(op);
-
- // Special-case the literal instructions.
- if(auto irConst = as<IRConstant>(inst))
- {
- switch (op)
- {
- case kIROp_IntLit:
- dump(context, irConst->value.intVal);
- return;
-
- case kIROp_FloatLit:
- dump(context, irConst->value.floatVal);
- return;
-
- case kIROp_BoolLit:
- dump(context, irConst->value.intVal ? "true" : "false");
- return;
-
- case kIROp_StringLit:
- dumpEncodeString(context, irConst->getStringSlice());
- return;
-
- case kIROp_PtrLit:
- dump(context, "<ptr>");
- return;
-
- default:
- break;
- }
- }
-
- dump(context, opInfo.name);
-
- UInt argCount = inst->getOperandCount();
-
- if(argCount == 0)
- return;
-
- UInt ii = 0;
-
- // Special case: make printing of `call` a bit
- // nicer to look at
- if (inst->op == kIROp_Call && argCount > 0)
- {
- dump(context, " ");
- auto argVal = inst->getOperand(ii++);
- dumpOperand(context, argVal);
- }
-
- bool first = true;
- dump(context, "(");
- for (; ii < argCount; ++ii)
- {
- if (!first)
- dump(context, ", ");
-
- auto argVal = inst->getOperand(ii);
-
- dumpOperand(context, argVal);
-
- first = false;
- }
-
- dump(context, ")");
-
- }
-
- static void dumpInstBody(
- IRDumpContext* context,
- IRInst* inst)
- {
- if (!inst)
- {
- dump(context, "<null>");
- return;
- }
-
- auto op = inst->op;
-
- dumpIRDecorations(context, inst);
-
- // 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_GlobalConstant:
- case kIROp_Generic:
- dumpIRGlobalValueWithCode(context, (IRGlobalValueWithCode*)inst);
- return;
-
- case kIROp_WitnessTable:
- case kIROp_StructType:
- dumpIRParentInst(context, inst);
- return;
-
- case kIROp_WitnessTableEntry:
- dumpIRWitnessTableEntry(context, (IRWitnessTableEntry*)inst);
- return;
-
- default:
- break;
- }
-
- // Okay, we have a seemingly "ordinary" op now
- auto dataType = inst->getDataType();
- auto rate = inst->getRate();
-
- if(rate)
- {
- dump(context, "@");
- dumpOperand(context, rate);
- dump(context, " ");
- }
-
- if(opHasResult(inst) || instHasUses(inst))
- {
- dump(context, "let ");
- dumpID(context, inst);
- dumpInstTypeClause(context, dataType);
- dump(context, "\t= ");
- }
- else
- {
- // No result, okay...
- }
-
- dumpInstExpr(context, inst);
- }
-
- static void dumpInst(
- IRDumpContext* context,
- IRInst* inst)
- {
- if(shouldFoldInstIntoUses(context, inst))
- return;
-
- dumpIndent(context);
- dumpInstBody(context, inst);
- dump(context, "\n");
- }
-
- void dumpIRModule(
- IRDumpContext* context,
- IRModule* module)
- {
- for(auto ii : module->getGlobalInsts())
- {
- dumpInst(context, ii);
- }
- }
-
- void printSlangIRAssembly(StringBuilder& builder, IRModule* module, IRDumpMode mode)
- {
- IRDumpContext context;
- context.builder = &builder;
- context.indent = 0;
- context.mode = mode;
-
- dumpIRModule(&context, module);
- }
-
- void dumpIR(IRInst* globalVal, ISlangWriter* writer, IRDumpMode mode)
- {
- StringBuilder sb;
-
- IRDumpContext context;
- context.builder = &sb;
- context.indent = 0;
- context.mode = mode;
-
- dumpInst(&context, globalVal);
-
- writer->write(sb.getBuffer(), sb.getLength());
- writer->flush();
- }
-
- String getSlangIRAssembly(IRModule* module, IRDumpMode mode)
- {
- StringBuilder sb;
- printSlangIRAssembly(sb, module, mode);
- return sb;
- }
-
- void dumpIR(IRModule* module, ISlangWriter* writer, IRDumpMode mode)
- {
- String ir = getSlangIRAssembly(module, mode);
- writer->write(ir.getBuffer(), ir.getLength());
- writer->flush();
- }
-
- // Pre-declare
- static bool _isTypeOperandEqual(IRInst* a, IRInst* b);
-
- 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;
- }
-
- // 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;
- }
-
- static bool _isNominalOp(IROp op)
- {
- // True if the op identity is 'nominal'
- switch (op)
- {
- case kIROp_StructType:
- case kIROp_InterfaceType:
- case kIROp_Generic:
- case kIROp_Param:
- {
- return true;
- }
- }
- 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)
- {
- if (a == b)
- {
- return true;
- }
-
- if (a == nullptr || b == nullptr)
- {
- return false;
- }
-
- const IROp opA = IROp(a->op & kIROpMeta_PseudoOpMask);
- const IROp opB = IROp(b->op & kIROpMeta_PseudoOpMask);
-
- 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.
-
- // If it's a resource type - special case the handling of the resource flavor
- if (IRResourceTypeBase::isaImpl(opA) &&
- static_cast<const IRResourceTypeBase*>(a)->getFlavor() != static_cast<const IRResourceTypeBase*>(b)->getFlavor())
- {
- return false;
- }
-
- // 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.
-
- // 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());
- }
-
- SLANG_ASSERT(!"Unhandled comparison");
-
- // We can't equate any other type..
- return false;
- }
-
- bool isTypeEqual(IRType* a, IRType* b)
- {
- // _isTypeOperandEqual handles comparison of types so can defer to it
- return _isTypeOperandEqual(a, b);
- }
-
- void findAllInstsBreadthFirst(IRInst* inst, List<IRInst*>& outInsts)
- {
- Index index = outInsts.getCount();
-
- outInsts.add(inst);
-
- 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::getLastDecoration()
- {
- IRDecoration* decoration = getFirstDecoration();
- if (!decoration) return nullptr;
-
- while (auto nextDecoration = decoration->getNextDecoration())
- decoration = nextDecoration;
-
- return decoration;
- }
-
- 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::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();
-
- return nullptr;
- }
-
- IRType* IRInst::getDataType()
- {
- auto type = getFullType();
- if(auto rateQualifiedType = as<IRRateQualifiedType>(type))
- return rateQualifiedType->getValueType();
-
- return type;
- }
-
- void IRInst::replaceUsesWith(IRInst* other)
- {
- // Safety check: don't try to replace something with itself.
- if(other == this)
- return;
-
- // We will walk through the list of uses for the current
- // instruction, and make them point to the other inst.
- IRUse* ff = firstUse;
-
- // No uses? Nothing to do.
- if(!ff)
- return;
-
- ff->debugValidate();
-
- IRUse* uu = ff;
- for(;;)
- {
- // The uses had better all be uses of this
- // instruction, or invariants are broken.
- SLANG_ASSERT(uu->get() == this);
-
- // Swap this use over to use the other value.
- uu->usedValue = other;
-
- // 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;
- }
-
- // 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;
-
- // And `this` will have no uses any more.
- this->firstUse = nullptr;
-
- ff->debugValidate();
- }
-
- // Insert this instruction into the same basic block
- // as `other`, right before it.
- void IRInst::insertBefore(IRInst* other)
- {
- SLANG_ASSERT(other);
- _insertAt(other->getPrevInst(), other, other->getParent());
- }
-
- void IRInst::insertAtStart(IRInst* newParent)
- {
- SLANG_ASSERT(newParent);
- _insertAt(nullptr, newParent->getFirstDecorationOrChild(), newParent);
- }
-
- void IRInst::moveToStart()
- {
- auto p = parent;
- removeFromParent();
- insertAtStart(p);
- }
-
- void IRInst::_insertAt(IRInst* inPrev, IRInst* inNext, IRInst* inParent)
- {
- // Make sure this instruction has been removed from any previous parent
- this->removeFromParent();
-
- SLANG_ASSERT(inParent);
- SLANG_ASSERT(!inPrev || (inPrev->getNextInst() == inNext) && (inPrev->getParent() == inParent));
- SLANG_ASSERT(!inNext || (inNext->getPrevInst() == inPrev) && (inNext->getParent() == inParent));
-
- if( inPrev )
- {
- inPrev->next = this;
- }
- else
- {
- inParent->m_decorationsAndChildren.first = this;
- }
-
- if (inNext)
- {
- inNext->prev = this;
- }
- else
- {
- inParent->m_decorationsAndChildren.last = this;
- }
-
- this->prev = inPrev;
- this->next = inNext;
- this->parent = inParent;
- }
-
- void IRInst::insertAfter(IRInst* other)
- {
- SLANG_ASSERT(other);
- removeFromParent();
- _insertAt(other, other->getNextInst(), other->getParent());
- }
-
- void IRInst::insertAtEnd(IRInst* newParent)
- {
- SLANG_ASSERT(newParent);
- removeFromParent();
- _insertAt(newParent->getLastDecorationOrChild(), nullptr, newParent);
- }
-
- void IRInst::moveToEnd()
- {
- auto p = parent;
- removeFromParent();
- insertAtEnd(p);
- }
-
- // Remove this instruction from its parent block,
- // and then destroy it (it had better have no uses!)
- void IRInst::removeFromParent()
- {
- auto oldParent = getParent();
-
- // If we don't currently have a parent, then
- // we are doing fine.
- if(!oldParent)
- return;
-
- auto pp = getPrevInst();
- auto nn = getNextInst();
-
- if(pp)
- {
- SLANG_ASSERT(pp->getParent() == oldParent);
- pp->next = nn;
- }
- else
- {
- oldParent->m_decorationsAndChildren.first = nn;
- }
-
- if(nn)
- {
- SLANG_ASSERT(nn->getParent() == oldParent);
- nn->prev = pp;
- }
- else
- {
- oldParent->m_decorationsAndChildren.last = pp;
- }
-
- prev = nullptr;
- next = nullptr;
- parent = nullptr;
- }
-
- void IRInst::removeArguments()
- {
- typeUse.clear();
- for( UInt aa = 0; aa < operandCount; ++aa )
- {
- IRUse& use = getOperands()[aa];
- use.clear();
- }
- }
-
- // Remove this instruction from its parent block,
- // and then destroy it (it had better have no uses!)
- void IRInst::removeAndDeallocate()
- {
- removeFromParent();
- removeArguments();
- removeAndDeallocateAllDecorationsAndChildren();
-
- // Run destructor to be sure...
- this->~IRInst();
- }
-
- void IRInst::removeAndDeallocateAllDecorationsAndChildren()
- {
- IRInst* nextChild = nullptr;
- for( IRInst* child = getFirstDecorationOrChild(); child; child = nextChild )
- {
- nextChild = child->getNextInst();
- child->removeAndDeallocate();
- }
- }
-
- void IRInst::transferDecorationsTo(IRInst* target)
- {
- while( auto decoration = getFirstDecoration() )
- {
- decoration->removeFromParent();
- decoration->insertAtStart(target);
- }
- }
-
- bool IRInst::mightHaveSideEffects()
- {
- // 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<IRType>(this))
- return false;
-
- if(as<IRConstant>(this))
- return false;
-
- switch(op)
- {
- // By default, assume that we might have side effects,
- // to safely cover all the instructions we haven't had time to think about.
- default:
- return true;
-
- 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);
- auto callee = getResolvedInstForDecorations(call->getCallee());
- if(callee->findDecoration<IRReadNoneDecoration>())
- {
- return false;
- }
- }
- return true;
-
- // All of the cases for "global values" are side-effect-free.
- case kIROp_StructType:
- case kIROp_StructField:
- case kIROp_Func:
- case kIROp_Generic:
- case kIROp_GlobalVar:
- case kIROp_GlobalConstant:
- case kIROp_GlobalParam:
- case kIROp_StructKey:
- case kIROp_GlobalGenericParam:
- case kIROp_WitnessTable:
- case kIROp_WitnessTableEntry:
- case kIROp_Block:
- return false;
-
- case kIROp_Nop:
- case kIROp_Specialize:
- case kIROp_lookup_interface_method:
- case kIROp_Construct:
- case kIROp_makeVector:
- case kIROp_MakeMatrix:
- case kIROp_makeArray:
- case kIROp_makeStruct:
- case kIROp_Load: // We are ignoring the possibility of loads from bad addresses, or `volatile` loads
- case kIROp_FieldExtract:
- case kIROp_FieldAddress:
- case kIROp_getElement:
- case kIROp_getElementPtr:
- case kIROp_constructVectorFromScalar:
- 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_Div: // TODO: We could split out integer vs. floating-point div/mod and assume the floating-point cases have no side effects
- //case kIROp_Mod:
- 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_Dot:
- case kIROp_Mul_Vector_Matrix:
- case kIROp_Mul_Matrix_Vector:
- case kIROp_Mul_Matrix_Matrix:
- case kIROp_MakeExistential:
- case kIROp_ExtractExistentialType:
- case kIROp_ExtractExistentialValue:
- case kIROp_ExtractExistentialWitnessTable:
- case kIROp_WrapExistential:
- return false;
- }
- }
-
- IRModule* IRInst::getModule()
- {
- IRInst* ii = this;
- while(ii)
- {
- if(auto moduleInst = as<IRModuleInst>(ii))
- return moduleInst->module;
-
- ii = ii->getParent();
- }
- return nullptr;
- }
-
- //
- // IRType
- //
-
- IRType* unwrapArray(IRType* type)
- {
- IRType* t = type;
- while( auto arrayType = as<IRArrayTypeBase>(t) )
- {
- t = arrayType->getElementType();
- }
- return t;
- }
-
- IRTargetIntrinsicDecoration* findTargetIntrinsicDecoration(
- IRInst* val,
- String const& targetName)
- {
- for(auto dd : val->getDecorations())
- {
- if(dd->op != kIROp_TargetIntrinsicDecoration)
- continue;
-
- auto decoration = (IRTargetIntrinsicDecoration*) dd;
- if(String(decoration->getTargetName()) == targetName)
- return decoration;
- }
-
- return nullptr;
- }
-
-#if 0
- IRFunc* cloneSimpleFuncWithoutRegistering(IRSpecContextBase* context, IRFunc* originalFunc)
- {
- auto clonedFunc = context->builder->createFunc();
- cloneFunctionCommon(context, clonedFunc, originalFunc, false);
- return clonedFunc;
- }
-#endif
-
- IRInst* findGenericReturnVal(IRGeneric* generic)
- {
- auto lastBlock = generic->getLastBlock();
- if (!lastBlock)
- return nullptr;
-
- auto returnInst = as<IRReturnVal>(lastBlock->getTerminator());
- if (!returnInst)
- return nullptr;
-
- auto val = returnInst->getVal();
- return val;
- }
-
- IRInst* getResolvedInstForDecorations(IRInst* inst)
- {
- IRInst* candidate = inst;
- while(auto specInst = as<IRSpecialize>(candidate))
- {
- auto genericInst = as<IRGeneric>(specInst->getBase());
- if(!genericInst)
- break;
-
- auto returnVal = findGenericReturnVal(genericInst);
- if(!returnVal)
- break;
-
- candidate = returnVal;
- }
- return candidate;
- }
-
- bool isDefinition(
- IRInst* inVal)
- {
- IRInst* val = inVal;
- // unwrap any generic declarations to see
- // the value they return.
- for(;;)
- {
- auto genericInst = as<IRGeneric>(val);
- if(!genericInst)
- break;
-
- auto returnVal = findGenericReturnVal(genericInst);
- if(!returnVal)
- break;
-
- val = returnVal;
- }
-
- // TODO: the logic here should probably
- // be that anything with an `IRImportDecoration`
- // is considered to be a declaration rather than definition.
-
- switch (val->op)
- {
- case kIROp_WitnessTable:
- case kIROp_GlobalConstant:
- case kIROp_Func:
- case kIROp_Generic:
- return val->getFirstChild() != nullptr;
-
- case kIROp_StructType:
- case kIROp_GlobalVar:
- case kIROp_GlobalParam:
- return true;
-
- default:
- return false;
- }
- }
-
- void markConstExpr(
- IRBuilder* builder,
- IRInst* irValue)
- {
- // We will take an IR value with type `T`,
- // and turn it into one with type `@ConstExpr T`.
-
- // TODO: need to be careful if the value already has a rate
- // qualifier set.
-
- irValue->setFullType(
- builder->getRateQualifiedType(
- builder->getConstExprRate(),
- irValue->getDataType()));
- }
-}