diff options
Diffstat (limited to 'source/slang/slang-ir-clone.cpp')
| -rw-r--r-- | source/slang/slang-ir-clone.cpp | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/source/slang/slang-ir-clone.cpp b/source/slang/slang-ir-clone.cpp new file mode 100644 index 000000000..df0555f9b --- /dev/null +++ b/source/slang/slang-ir-clone.cpp @@ -0,0 +1,295 @@ +// slang-ir-clone.cpp +#include "slang-ir-clone.h" + +#include "slang-ir.h" +#include "slang-ir-insts.h" + +namespace Slang +{ + +IRInst* lookUp(IRCloneEnv* env, IRInst* oldVal) +{ + for( auto ee = env; ee; ee = ee->parent ) + { + IRInst* newVal = nullptr; + if(ee->mapOldValToNew.TryGetValue(oldVal, newVal)) + return newVal; + } + return nullptr; +} + +IRInst* findCloneForOperand( + IRCloneEnv* env, + IRInst* oldOperand) +{ + if(!oldOperand) return nullptr; + + // If there is a registered replacement for + // the existing operand, then use it. + // + if( IRInst* newVal = lookUp(env, oldOperand) ) + return newVal; + + // Otherwise, we assume that the caller wants + // to default to using existing values wherever + // an explicit replacement hasn't been registered. + // + // This is, notably, the right default whenever + // `oldOperand` is a global value or constant + // and our cloned code will sit in the same + // module as the original. + // + // TODO: We could make this a customization point + // down the road, if we ever had a case where + // we want to clone things with a different policy. + // + return oldOperand; +} + +IRInst* cloneInstAndOperands( + IRCloneEnv* env, + IRBuilder* builder, + IRInst* oldInst) +{ + SLANG_ASSERT(env); + SLANG_ASSERT(builder); + SLANG_ASSERT(oldInst); + + // This logic will not handle any instructions + // with special-case data attached, but that only + // applies to `IRConstant`s at this point, and those + // should only appear at the global scope rather than + // in function bodies. + // + // TODO: It would be easy enough to extend this logic + // to handle constants gracefully, if it ever comes up. + // + SLANG_ASSERT(!as<IRConstant>(oldInst)); + + // We start by mapping the type of the orignal instruction + // to its replacement value, if any. + // + auto oldType = oldInst->getFullType(); + auto newType = (IRType*) findCloneForOperand(env, oldType); + + // Next we will create an empty shell of the instruction, + // with space for the operands, but no actual operand + // values attached. + // + UInt operandCount = oldInst->getOperandCount(); + auto newInst = builder->emitIntrinsicInst( + newType, + oldInst->op, + operandCount, + nullptr); + + // Finally we will iterate over the operands of `oldInst` + // to find their replacements and install them as + // the operands of `newInst`. + // + for(UInt ii = 0; ii < operandCount; ++ii) + { + auto oldOperand = oldInst->getOperand(ii); + auto newOperand = findCloneForOperand(env, oldOperand); + + newInst->getOperands()[ii].init(newInst, newOperand); + } + + return newInst; +} + +// The complexity of the second phase of cloning (the +// one that deals with decorations and children) comes +// from the fact that it needs to sequence the two phases +// of cloning for any child instructions. We will do this +// by performing the first phase of cloning, and building +// up a list of children that require the second phase of processing. +// Each entry in that list will be a pair of an old instruction +// and its new clone. +// +struct IRCloningOldNewPair +{ + IRInst* oldInst; + IRInst* newInst; +}; + +// We will use an internal variant of `cloneInstDecorationsAndChildren` +// that modifies the provided `env` as it goes as the main +// workhorse, since we need to make sure that instructions in +// earlier blocks are visible to those in other, later, blocks +// when cloning a function, so that strict scoping along the +// lines of the nesting of instructions isn't sufficient. +// +static void _cloneInstDecorationsAndChildren( + IRCloneEnv* env, + SharedIRBuilder* sharedBuilder, + IRInst* oldInst, + IRInst* newInst) +{ + SLANG_ASSERT(env); + SLANG_ASSERT(sharedBuilder); + SLANG_ASSERT(oldInst); + SLANG_ASSERT(newInst); + + // We will set up an IR builder that inserts + // into the new parent instruction. + // + IRBuilder builderStorage; + auto builder = &builderStorage; + builder->sharedBuilder = sharedBuilder; + builder->setInsertInto(newInst); + + // When applying the first phase of cloning to + // children, we will keep track of those that + // require the second phase. + // + List<IRCloningOldNewPair> pairs; + + for( auto oldChild : oldInst->getDecorationsAndChildren() ) + { + // As a very subtle special case, if one of the children + // of our `oldInst` already has a registered replacement, + // then we don't want to clone it (not least because + // the `Dictionary::Add` method would give us an error + // when we try to insert a new value for the same key). + // + // This arises for entries in `mapOldValToNew` that were + // seeded before cloning begain (e.g., function + // parameters that are to be replaced). + // + if(lookUp(env, oldChild)) + continue; + + // Now we can perform the first phase of cloning + // on the child, and register it in our map from + // old to new values. + // + auto newChild = cloneInstAndOperands(env, builder, oldChild); + env->mapOldValToNew.Add(oldChild, newChild); + + // If and only if the old child had decorations + // or children, we will register it into our + // list for processing in the second phase. + // + if( oldChild->getFirstDecorationOrChild() ) + { + IRCloningOldNewPair pair; + pair.oldInst = oldChild; + pair.newInst = newChild; + pairs.add(pair); + } + } + + // Once we have done first-phase processing for + // all child instructions, we scan through those + // in the list that required second-phase processing, + // and clone their decorations and/or children recursively. + // + for( auto pair : pairs ) + { + auto oldChild = pair.oldInst; + auto newChild = pair.newInst; + + _cloneInstDecorationsAndChildren(env, sharedBuilder, oldChild, newChild); + } +} + +// The public version of `cloneInstDecorationsAndChildren` is then +// just a wrapper over the internal one that sets up a temporary +// environment to use for the cloning process, so that we do +// not leave any lasting changes in the user-provided `env`. +// +void cloneInstDecorationsAndChildren( + IRCloneEnv* env, + SharedIRBuilder* sharedBuilder, + IRInst* oldInst, + IRInst* newInst) +{ + SLANG_ASSERT(sharedBuilder); + SLANG_ASSERT(oldInst); + SLANG_ASSERT(newInst); + + IRCloneEnv subEnvStorage; + auto subEnv = &subEnvStorage; + subEnv->parent = env; + + _cloneInstDecorationsAndChildren(subEnv, sharedBuilder, oldInst, newInst); +} + +// The convenience function `cloneInst` just sequences the +// operations that have already been defined. +// +IRInst* cloneInst( + IRCloneEnv* env, + IRBuilder* builder, + IRInst* oldInst) +{ + SLANG_ASSERT(env); + SLANG_ASSERT(builder); + SLANG_ASSERT(oldInst); + + auto newInst = cloneInstAndOperands( + env, builder, oldInst); + + env->mapOldValToNew.Add(oldInst, newInst); + + cloneInstDecorationsAndChildren( + env, builder->sharedBuilder, oldInst, newInst); + + return newInst; +} + +void cloneDecoration( + IRDecoration* oldDecoration, + IRInst* newParent, + IRModule* module) +{ + SharedIRBuilder sharedBuilder; + sharedBuilder.module = module; + + IRBuilder builder; + builder.sharedBuilder = &sharedBuilder; + + if(auto first = newParent->getFirstDecorationOrChild()) + builder.setInsertBefore(first); + else + builder.setInsertInto(newParent); + + IRCloneEnv env; + cloneInst(&env, &builder, oldDecoration); +} + +void cloneDecoration( + IRDecoration* oldDecoration, + IRInst* newParent) +{ + cloneDecoration( + oldDecoration, + newParent, + newParent->getModule()); +} + +bool IRSimpleSpecializationKey::operator==(IRSimpleSpecializationKey const& other) const +{ + auto valCount = vals.getCount(); + if(valCount != other.vals.getCount()) return false; + for( Index ii = 0; ii < valCount; ++ii ) + { + if(vals[ii] != other.vals[ii]) return false; + } + return true; +} + +int IRSimpleSpecializationKey::GetHashCode() const +{ + auto valCount = vals.getCount(); + int hash = Slang::GetHashCode(valCount); + for( Index ii = 0; ii < valCount; ++ii ) + { + hash = combineHash(hash, Slang::GetHashCode(vals[ii])); + } + return hash; +} + + +} // namespace Slang |
