summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-ir-link.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/slang-ir-link.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/slang-ir-link.cpp')
-rw-r--r--source/slang/slang-ir-link.cpp1361
1 files changed, 1361 insertions, 0 deletions
diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp
new file mode 100644
index 000000000..4c1f72adb
--- /dev/null
+++ b/source/slang/slang-ir-link.cpp
@@ -0,0 +1,1361 @@
+// slang-ir-link.cpp
+#include "slang-ir-link.h"
+
+#include "slang-ir.h"
+#include "slang-ir-insts.h"
+#include "slang-mangle.h"
+
+namespace Slang
+{
+
+// Needed for lookup up entry-point layouts.
+//
+// TODO: maybe arrange so that codegen is driven from the layout layer
+// instead of the input/request layer.
+EntryPointLayout* findEntryPointLayout(
+ ProgramLayout* programLayout,
+ EntryPoint* EntryPoint);
+
+struct IRSpecSymbol : RefObject
+{
+ IRInst* irGlobalValue;
+ RefPtr<IRSpecSymbol> nextWithSameName;
+};
+
+struct IRSpecEnv
+{
+ IRSpecEnv* parent = nullptr;
+
+ // A map from original values to their cloned equivalents.
+ typedef Dictionary<IRInst*, IRInst*> ClonedValueDictionary;
+ ClonedValueDictionary clonedValues;
+};
+
+struct IRSharedSpecContext
+{
+ // The code-generation target in use
+ CodeGenTarget target;
+
+ // The specialized module we are building
+ RefPtr<IRModule> module;
+
+ // A map from mangled symbol names to zero or
+ // more global IR values that have that name,
+ // in the *original* module.
+ typedef Dictionary<String, RefPtr<IRSpecSymbol>> SymbolDictionary;
+ SymbolDictionary symbols;
+
+ SharedIRBuilder sharedBuilderStorage;
+ IRBuilder builderStorage;
+
+ // The "global" specialization environment.
+ IRSpecEnv globalEnv;
+};
+
+struct IRSpecContextBase
+{
+ // A map from the mangled name of a global variable
+ // to the layout to use for it.
+ Dictionary<String, VarLayout*> globalVarLayouts;
+
+ IRSharedSpecContext* shared;
+
+ IRSharedSpecContext* getShared() { return shared; }
+
+ IRModule* getModule() { return getShared()->module; }
+
+ IRSharedSpecContext::SymbolDictionary& getSymbols() { return getShared()->symbols; }
+
+ // The current specialization environment to use.
+ IRSpecEnv* env = nullptr;
+ IRSpecEnv* getEnv()
+ {
+ // TODO: need to actually establish environments on contexts we create.
+ //
+ // Or more realistically we need to change the whole approach
+ // to specialization and cloning so that we don't try to share
+ // logic between two very different cases.
+
+
+ return env;
+ }
+
+ // The IR builder to use for creating nodes
+ IRBuilder* builder;
+
+ // A callback to be used when a value that is not registerd in `clonedValues`
+ // is needed during cloning. This gives the subtype a chance to intercept
+ // the operation and clone (or not) as needed.
+ virtual IRInst* maybeCloneValue(IRInst* originalVal)
+ {
+ return originalVal;
+ }
+};
+
+void registerClonedValue(
+ IRSpecContextBase* context,
+ IRInst* clonedValue,
+ IRInst* originalValue)
+{
+ if(!originalValue)
+ return;
+
+ // TODO: now that things are scoped using environments, we
+ // shouldn't be running into the cases where a value with
+ // the same key already exists. This should be changed to
+ // an `Add()` call.
+ //
+ context->getEnv()->clonedValues[originalValue] = clonedValue;
+}
+
+// Information on values to use when registering a cloned value
+struct IROriginalValuesForClone
+{
+ IRInst* originalVal = nullptr;
+ IRSpecSymbol* sym = nullptr;
+
+ IROriginalValuesForClone() {}
+
+ IROriginalValuesForClone(IRInst* originalValue)
+ : originalVal(originalValue)
+ {}
+
+ IROriginalValuesForClone(IRSpecSymbol* symbol)
+ : sym(symbol)
+ {}
+};
+
+void registerClonedValue(
+ IRSpecContextBase* context,
+ IRInst* clonedValue,
+ IROriginalValuesForClone const& originalValues)
+{
+ registerClonedValue(context, clonedValue, originalValues.originalVal);
+ for( auto s = originalValues.sym; s; s = s->nextWithSameName )
+ {
+ registerClonedValue(context, clonedValue, s->irGlobalValue);
+ }
+}
+
+IRInst* cloneInst(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRInst* originalInst,
+ IROriginalValuesForClone const& originalValues);
+
+IRInst* cloneInst(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRInst* originalInst)
+{
+ return cloneInst(context, builder, originalInst, originalInst);
+}
+
+ /// Clone any decorations from `originalValue` onto `clonedValue`
+void cloneDecorations(
+ IRSpecContextBase* context,
+ IRInst* clonedValue,
+ IRInst* originalValue)
+{
+ // TODO: In many cases we might be able to use this as a general-purpose
+ // place to do cloning of *all* the children of an instruction, and
+ // not just its decorations. We should look to refactor this code
+ // later.
+
+ IRBuilder builderStorage = *context->builder;
+ IRBuilder* builder = &builderStorage;
+ builder->setInsertInto(clonedValue);
+
+
+ SLANG_UNUSED(context);
+ for(auto originalDecoration : originalValue->getDecorations())
+ {
+ cloneInst(context, builder, originalDecoration);
+ }
+
+ // We will also clone the location here, just because this is a convenient bottleneck
+ clonedValue->sourceLoc = originalValue->sourceLoc;
+}
+
+ /// Clone any decorations and children from `originalValue` onto `clonedValue`
+void cloneDecorationsAndChildren(
+ IRSpecContextBase* context,
+ IRInst* clonedValue,
+ IRInst* originalValue)
+{
+ IRBuilder builderStorage = *context->builder;
+ IRBuilder* builder = &builderStorage;
+ builder->setInsertInto(clonedValue);
+
+ SLANG_UNUSED(context);
+ for(auto originalItem : originalValue->getDecorationsAndChildren())
+ {
+ cloneInst(context, builder, originalItem);
+ }
+
+ // We will also clone the location here, just because this is a convenient bottleneck
+ clonedValue->sourceLoc = originalValue->sourceLoc;
+}
+
+// We use an `IRSpecContext` for the case where we are cloning
+// code from one or more input modules to create a "linked" output
+// module. Along the way, we will resolve profile-specific functions
+// to the best definition for a given target.
+//
+struct IRSpecContext : IRSpecContextBase
+{
+ // Override the "maybe clone" logic so that we always clone
+ virtual IRInst* maybeCloneValue(IRInst* originalVal) override;
+};
+
+
+IRInst* cloneGlobalValue(IRSpecContext* context, IRInst* originalVal);
+
+IRInst* cloneValue(
+ IRSpecContextBase* context,
+ IRInst* originalValue);
+
+IRType* cloneType(
+ IRSpecContextBase* context,
+ IRType* originalType);
+
+IRInst* IRSpecContext::maybeCloneValue(IRInst* originalValue)
+{
+ switch (originalValue->op)
+ {
+ case kIROp_StructType:
+ case kIROp_Func:
+ case kIROp_Generic:
+ case kIROp_GlobalVar:
+ case kIROp_GlobalConstant:
+ case kIROp_GlobalParam:
+ case kIROp_StructKey:
+ case kIROp_GlobalGenericParam:
+ case kIROp_WitnessTable:
+ return cloneGlobalValue(this, originalValue);
+
+ case kIROp_BoolLit:
+ {
+ IRConstant* c = (IRConstant*)originalValue;
+ return builder->getBoolValue(c->value.intVal != 0);
+ }
+ break;
+
+
+ case kIROp_IntLit:
+ {
+ IRConstant* c = (IRConstant*)originalValue;
+ return builder->getIntValue(cloneType(this, c->getDataType()), c->value.intVal);
+ }
+ break;
+
+ case kIROp_FloatLit:
+ {
+ IRConstant* c = (IRConstant*)originalValue;
+ return builder->getFloatValue(cloneType(this, c->getDataType()), c->value.floatVal);
+ }
+ break;
+
+ case kIROp_StringLit:
+ {
+ IRConstant* c = (IRConstant*)originalValue;
+ return builder->getStringValue(c->getStringSlice());
+ }
+ break;
+
+ case kIROp_PtrLit:
+ {
+ IRConstant* c = (IRConstant*)originalValue;
+ return builder->getPtrValue(c->value.ptrVal);
+ }
+ break;
+
+ default:
+ {
+ // In the deafult case, assume that we have some sort of "hoistable"
+ // instruction that requires us to create a clone of it.
+
+ UInt argCount = originalValue->getOperandCount();
+ IRInst* clonedValue = builder->createIntrinsicInst(
+ cloneType(this, originalValue->getFullType()),
+ originalValue->op,
+ argCount, nullptr);
+ registerClonedValue(this, clonedValue, originalValue);
+ for (UInt aa = 0; aa < argCount; ++aa)
+ {
+ IRInst* originalArg = originalValue->getOperand(aa);
+ IRInst* clonedArg = cloneValue(this, originalArg);
+ clonedValue->getOperands()[aa].init(clonedValue, clonedArg);
+ }
+ cloneDecorationsAndChildren(this, clonedValue, originalValue);
+
+ addHoistableInst(builder, clonedValue);
+
+ return clonedValue;
+ }
+ break;
+ }
+}
+
+IRInst* cloneValue(
+ IRSpecContextBase* context,
+ IRInst* originalValue);
+
+// Find a pre-existing cloned value, or return null if none is available.
+IRInst* findClonedValue(
+ IRSpecContextBase* context,
+ IRInst* originalValue)
+{
+ IRInst* clonedValue = nullptr;
+ for (auto env = context->getEnv(); env; env = env->parent)
+ {
+ if (env->clonedValues.TryGetValue(originalValue, clonedValue))
+ {
+ return clonedValue;
+ }
+ }
+
+ return nullptr;
+}
+
+IRInst* cloneValue(
+ IRSpecContextBase* context,
+ IRInst* originalValue)
+{
+ if (!originalValue)
+ return nullptr;
+
+ if (IRInst* clonedValue = findClonedValue(context, originalValue))
+ return clonedValue;
+
+ return context->maybeCloneValue(originalValue);
+}
+
+IRType* cloneType(
+ IRSpecContextBase* context,
+ IRType* originalType)
+{
+ return (IRType*)cloneValue(context, originalType);
+}
+
+void cloneGlobalValueWithCodeCommon(
+ IRSpecContextBase* context,
+ IRGlobalValueWithCode* clonedValue,
+ IRGlobalValueWithCode* originalValue);
+
+IRRate* cloneRate(
+ IRSpecContextBase* context,
+ IRRate* rate)
+{
+ return (IRRate*) cloneType(context, rate);
+}
+
+void maybeSetClonedRate(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRInst* clonedValue,
+ IRInst* originalValue)
+{
+ if(auto rate = originalValue->getRate() )
+ {
+ clonedValue->setFullType(builder->getRateQualifiedType(
+ cloneRate(context, rate),
+ clonedValue->getFullType()));
+ }
+}
+
+IRGlobalVar* cloneGlobalVarImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRGlobalVar* originalVar,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedVar = builder->createGlobalVar(
+ cloneType(context, originalVar->getDataType()->getValueType()));
+
+ maybeSetClonedRate(context, builder, clonedVar, originalVar);
+
+ registerClonedValue(context, clonedVar, originalValues);
+
+ // Clone any code in the body of the variable, since this
+ // represents the initializer.
+ cloneGlobalValueWithCodeCommon(
+ context,
+ clonedVar,
+ originalVar);
+
+ return clonedVar;
+}
+
+IRGlobalConstant* cloneGlobalConstantImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRGlobalConstant* originalVal,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedVal = builder->createGlobalConstant(
+ cloneType(context, originalVal->getFullType()));
+ registerClonedValue(context, clonedVal, originalValues);
+
+ // Clone any code in the body of the constant, since this
+ // represents the initializer.
+ cloneGlobalValueWithCodeCommon(
+ context,
+ clonedVal,
+ originalVal);
+
+ return clonedVal;
+}
+
+void cloneSimpleGlobalValueImpl(
+ IRSpecContextBase* context,
+ IRInst* originalInst,
+ IROriginalValuesForClone const& originalValues,
+ IRInst* clonedInst,
+ bool registerValue = true)
+{
+ if (registerValue)
+ registerClonedValue(context, clonedInst, originalValues);
+
+ // Set up an IR builder for inserting into the inst
+ IRBuilder builderStorage = *context->builder;
+ IRBuilder* builder = &builderStorage;
+ builder->setInsertInto(clonedInst);
+
+ // Clone any children of the instruction
+ for (auto child : originalInst->getDecorationsAndChildren())
+ {
+ cloneInst(context, builder, child);
+ }
+}
+
+IRGlobalParam* cloneGlobalParamImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRGlobalParam* originalVal,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedVal = builder->createGlobalParam(
+ cloneType(context, originalVal->getFullType()));
+ cloneSimpleGlobalValueImpl(context, originalVal, originalValues, clonedVal);
+
+ if(auto linkage = originalVal->findDecoration<IRLinkageDecoration>())
+ {
+ auto mangledName = String(linkage->getMangledName());
+ VarLayout* layout = nullptr;
+ if (context->globalVarLayouts.TryGetValue(mangledName, layout))
+ {
+ builder->addLayoutDecoration(clonedVal, layout);
+ }
+ }
+
+ return clonedVal;
+}
+
+IRGeneric* cloneGenericImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRGeneric* originalVal,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedVal = builder->emitGeneric();
+ registerClonedValue(context, clonedVal, originalValues);
+
+ // Clone any code in the body of the generic, since this
+ // computes its result value.
+ cloneGlobalValueWithCodeCommon(
+ context,
+ clonedVal,
+ originalVal);
+
+ return clonedVal;
+}
+
+IRStructKey* cloneStructKeyImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRStructKey* originalVal,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedVal = builder->createStructKey();
+ cloneSimpleGlobalValueImpl(context, originalVal, originalValues, clonedVal);
+ return clonedVal;
+}
+
+IRGlobalGenericParam* cloneGlobalGenericParamImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRGlobalGenericParam* originalVal,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedVal = builder->emitGlobalGenericParam();
+ cloneSimpleGlobalValueImpl(context, originalVal, originalValues, clonedVal);
+ return clonedVal;
+}
+
+
+IRWitnessTable* cloneWitnessTableImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRWitnessTable* originalTable,
+ IROriginalValuesForClone const& originalValues,
+ IRWitnessTable* dstTable = nullptr,
+ bool registerValue = true)
+{
+ auto clonedTable = dstTable ? dstTable : builder->createWitnessTable();
+ cloneSimpleGlobalValueImpl(context, originalTable, originalValues, clonedTable, registerValue);
+ return clonedTable;
+}
+
+IRWitnessTable* cloneWitnessTableWithoutRegistering(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRWitnessTable* originalTable,
+ IRWitnessTable* dstTable = nullptr)
+{
+ return cloneWitnessTableImpl(context, builder, originalTable, IROriginalValuesForClone(), dstTable, false);
+}
+
+IRStructType* cloneStructTypeImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRStructType* originalStruct,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedStruct = builder->createStructType();
+ cloneSimpleGlobalValueImpl(context, originalStruct, originalValues, clonedStruct);
+ return clonedStruct;
+}
+
+
+IRInterfaceType* cloneInterfaceTypeImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRInterfaceType* originalInterface,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedInterface = builder->createInterfaceType();
+ cloneSimpleGlobalValueImpl(context, originalInterface, originalValues, clonedInterface);
+ return clonedInterface;
+}
+
+void cloneGlobalValueWithCodeCommon(
+ IRSpecContextBase* context,
+ IRGlobalValueWithCode* clonedValue,
+ IRGlobalValueWithCode* originalValue)
+{
+ // Next we are going to clone the actual code.
+ IRBuilder builderStorage = *context->builder;
+ IRBuilder* builder = &builderStorage;
+ builder->setInsertInto(clonedValue);
+
+ cloneDecorations(context, clonedValue, originalValue);
+
+ // We will walk through the blocks of the function, and clone each of them.
+ //
+ // We need to create the cloned blocks first, and then walk through them,
+ // because blocks might be forward referenced (this is not possible
+ // for other cases of instructions).
+ for (auto originalBlock = originalValue->getFirstBlock();
+ originalBlock;
+ originalBlock = originalBlock->getNextBlock())
+ {
+ IRBlock* clonedBlock = builder->createBlock();
+ clonedValue->addBlock(clonedBlock);
+ registerClonedValue(context, clonedBlock, originalBlock);
+
+#if 0
+ // We can go ahead and clone parameters here, while we are at it.
+ builder->curBlock = clonedBlock;
+ for (auto originalParam = originalBlock->getFirstParam();
+ originalParam;
+ originalParam = originalParam->getNextParam())
+ {
+ IRParam* clonedParam = builder->emitParam(
+ context->maybeCloneType(
+ originalParam->getFullType()));
+ cloneDecorations(context, clonedParam, originalParam);
+ registerClonedValue(context, clonedParam, originalParam);
+ }
+#endif
+ }
+
+ // Okay, now we are in a good position to start cloning
+ // the instructions inside the blocks.
+ {
+ IRBlock* ob = originalValue->getFirstBlock();
+ IRBlock* cb = clonedValue->getFirstBlock();
+ while (ob)
+ {
+ SLANG_ASSERT(cb);
+
+ builder->setInsertInto(cb);
+ for (auto oi = ob->getFirstInst(); oi; oi = oi->getNextInst())
+ {
+ cloneInst(context, builder, oi);
+ }
+
+ ob = ob->getNextBlock();
+ cb = cb->getNextBlock();
+ }
+ }
+
+}
+
+void checkIRDuplicate(IRInst* inst, IRInst* moduleInst, UnownedStringSlice const& mangledName)
+{
+#ifdef _DEBUG
+ for (auto child : moduleInst->getDecorationsAndChildren())
+ {
+ if (child == inst)
+ continue;
+
+ if(auto childLinkage = child->findDecoration<IRLinkageDecoration>())
+ {
+ if(mangledName == childLinkage->getMangledName())
+ {
+ SLANG_UNEXPECTED("duplicate global instruction");
+ }
+ }
+ }
+#else
+ SLANG_UNREFERENCED_PARAMETER(inst);
+ SLANG_UNREFERENCED_PARAMETER(moduleInst);
+ SLANG_UNREFERENCED_PARAMETER(mangledName);
+#endif
+}
+
+void cloneFunctionCommon(
+ IRSpecContextBase* context,
+ IRFunc* clonedFunc,
+ IRFunc* originalFunc,
+ bool checkDuplicate = true)
+{
+ // First clone all the simple properties.
+ clonedFunc->setFullType(cloneType(context, originalFunc->getFullType()));
+
+ cloneGlobalValueWithCodeCommon(
+ context,
+ clonedFunc,
+ originalFunc);
+
+ // Shuffle the function to the end of the list, because
+ // it needs to follow its dependencies.
+ //
+ // TODO: This isn't really a good requirement to place on the IR...
+ clonedFunc->moveToEnd();
+
+ if( checkDuplicate )
+ {
+ if( auto linkage = clonedFunc->findDecoration<IRLinkageDecoration>() )
+ {
+ checkIRDuplicate(clonedFunc, context->getModule()->getModuleInst(), linkage->getMangledName());
+ }
+ }
+}
+
+// We will forward-declare the subroutine for eagerly specializing
+// an IR-level generic to argument values, because `specializeIRForEntryPoint`
+// needs to perform this operation even though it is logically part of
+// the later generic specialization pass.
+//
+IRInst* specializeGeneric(
+ IRSpecialize* specializeInst);
+
+IRFunc* specializeIRForEntryPoint(
+ IRSpecContext* context,
+ EntryPoint* entryPoint,
+ EntryPointLayout* entryPointLayout)
+{
+ // We start by looking up the IR symbol that
+ // matches the mangled name given to the
+ // function we want to emit.
+ //
+ // Note: the function decl-ref may refer to
+ // a specialization of a generic function,
+ // so that the mangled name of the decl-ref is
+ // not the same as the mangled name of the decl.
+ //
+ auto mangledName = getMangledName(entryPoint->getFuncDeclRef());
+ RefPtr<IRSpecSymbol> sym;
+ if (!context->getSymbols().TryGetValue(mangledName, sym))
+ {
+ SLANG_UNEXPECTED("no matching IR symbol");
+ return nullptr;
+ }
+
+ // TODO: deal with the case where we might
+ // have multiple (profile-overloaded) versions...
+ //
+ auto originalVal = sym->irGlobalValue;
+
+ // We will start by cloning the entry point reference
+ // like any other global value.
+ //
+ auto clonedVal = cloneGlobalValue(context, originalVal);
+
+ // In the case where the user is requesting a specialization
+ // of a generic entry point, we have a bit of a problem.
+ //
+ // This function is expected to return an `IRFunc` and
+ // subsequent passes expect to find, e.g., layout information
+ // attached to the parameters of such a func.
+ //
+ // In the generic case, the `clonedValue` won't be an
+ // `IRFunc`, but instead an `IRSpecialize`.
+ //
+ if(auto clonedSpec = as<IRSpecialize>(clonedVal))
+ {
+ // The Right Thing to do here is to perform some
+ // amount of generic specialization, at least
+ // until we get back an `IRFunc`.
+ //
+ // The dangerous thing is that the generic specialization
+ // pass can, in principle, change the signature of
+ // functions, so that attaching parameter layout
+ // information *after* specialization might not work.
+ //
+ // The compromise we make here is to directly
+ // invoke the logic for specializing a generic.
+ //
+ // In theory this isn't valid, because there is no
+ // way we can register the specialized function we
+ // create so that it would be re-used by other instantiations
+ // with the same arguments (because we cannot be
+ // sure the generic arguments are themselves fully specialized)
+ //
+ // In practice this isn't really a problem, because
+ // we don't want to share the definition between
+ // an entry point and an ordinary function anyway.
+ //
+ clonedVal = specializeGeneric(clonedSpec);
+ }
+
+ // TODO: If there is an existential-related decoration
+ // on the entry point, we need to transfer it over
+ // to the specialized function.
+ if( auto bindExistentialSlots = originalVal->findDecorationImpl(kIROp_BindExistentialSlotsDecoration) )
+ {
+ if( !clonedVal->findDecorationImpl(kIROp_BindExistentialSlotsDecoration) )
+ {
+ IRBuilder builderStorage = *context->builder;
+ IRBuilder* builder = &builderStorage;
+ builder->setInsertInto(clonedVal);
+
+ auto clonedBind = cloneInst(context, builder, bindExistentialSlots);
+ clonedBind->moveToStart();
+ }
+ }
+
+
+ auto clonedFunc = as<IRFunc>(clonedVal);
+ if(!clonedFunc)
+ {
+ SLANG_UNEXPECTED("expected entry point to be a function");
+ return nullptr;
+ }
+
+ if( !clonedFunc->findDecorationImpl(kIROp_KeepAliveDecoration) )
+ {
+ context->builder->addKeepAliveDecoration(clonedFunc);
+ }
+
+ // We need to attach the layout information for
+ // the entry point to this declaration, so that
+ // we can use it to inform downstream code emit.
+ //
+ context->builder->addLayoutDecoration(
+ clonedFunc,
+ entryPointLayout);
+
+ // We will also go on and attach layout information
+ // to the function parameters, so that we have it
+ // available directly on the parameters, rather
+ // than having to look it up on the original entry-point layout.
+ if( auto firstBlock = clonedFunc->getFirstBlock() )
+ {
+ auto paramsStructLayout = getScopeStructLayout(entryPointLayout);
+ Index paramLayoutCount = paramsStructLayout->fields.getCount();
+ Index paramCounter = 0;
+ for( auto pp = firstBlock->getFirstParam(); pp; pp = pp->getNextParam() )
+ {
+ Index paramIndex = paramCounter++;
+ if( paramIndex < paramLayoutCount )
+ {
+ auto paramLayout = paramsStructLayout->fields[paramIndex];
+ context->builder->addLayoutDecoration(
+ pp,
+ paramLayout);
+ }
+ else
+ {
+ SLANG_UNEXPECTED("too many parameters");
+ }
+ }
+ }
+
+ return clonedFunc;
+}
+
+// Get a string form of the target so that we can
+// use it to match against target-specialization modifiers
+//
+// TODO: We shouldn't be using strings for this.
+String getTargetName(IRSpecContext* context)
+{
+ switch( context->shared->target )
+ {
+ case CodeGenTarget::HLSL:
+ return "hlsl";
+
+ case CodeGenTarget::GLSL:
+ return "glsl";
+
+ case CodeGenTarget::CSource:
+ return "c";
+
+ case CodeGenTarget::CPPSource:
+ return "cpp";
+
+ default:
+ SLANG_UNEXPECTED("unhandled case");
+ UNREACHABLE_RETURN("unknown");
+ }
+}
+
+// How specialized is a given declaration for the chosen target?
+enum class TargetSpecializationLevel
+{
+ specializedForOtherTarget = 0,
+ notSpecialized,
+ specializedForTarget,
+};
+
+TargetSpecializationLevel getTargetSpecialiationLevel(
+ IRInst* inVal,
+ String const& targetName)
+{
+ // HACK: Currently the front-end is placing modifiers related
+ // to target specialization on nodes like functions, even when
+ // those functions are being returned by a generic. This
+ // means that we need to try and inspect the value being
+ // returned by the generic if we are looking at a generic.
+ IRInst* val = inVal;
+ while( auto genericVal = as<IRGeneric>(val) )
+ {
+ auto firstBlock = genericVal->getFirstBlock();
+ if(!firstBlock) break;
+
+ auto returnInst = as<IRReturnVal>(firstBlock->getLastInst());
+ if(!returnInst) break;
+
+ val = returnInst->getVal();
+ }
+
+ TargetSpecializationLevel result = TargetSpecializationLevel::notSpecialized;
+ for(auto dd : val->getDecorations())
+ {
+ if(dd->op != kIROp_TargetDecoration)
+ continue;
+
+ auto decoration = (IRTargetDecoration*) dd;
+ if(String(decoration->getTargetName()) == targetName)
+ return TargetSpecializationLevel::specializedForTarget;
+
+ result = TargetSpecializationLevel::specializedForOtherTarget;
+ }
+
+ return result;
+}
+
+// Is `newVal` marked as being a better match for our
+// chosen code-generation target?
+//
+// TODO: there is a missing step here where we need
+// to check if things are even available in the first place...
+bool isBetterForTarget(
+ IRSpecContext* context,
+ IRInst* newVal,
+ IRInst* oldVal)
+{
+ String targetName = getTargetName(context);
+
+ // For right now every declaration might have zero or more
+ // modifiers, representing the targets for which it is specialized.
+ // Each modifier has a single string "tag" to represent a target.
+ // We thus decide that a declaration is "more specialized" by:
+ //
+ // - Does it have a modifier with a tag with the string for the current target?
+ // If yes, it is the most specialized it can be.
+ //
+ // - Does it have a no tags? Then it is "unspecialized" and that is okay.
+ //
+ // - Does it have a modifier with a tag for a *different* target?
+ // If yes, then it shouldn't even be usable on this target.
+ //
+ // Longer term a better approach is to think of this in terms
+ // of a "disjunction of conjunctions" that is:
+ //
+ // (A and B and C) or (A and D) or (E) or (F and G) ...
+ //
+ // A code generation target would then consist of a
+ // conjunction of invidual tags:
+ //
+ // (HLSL and SM_4_0 and Vertex and ...)
+ //
+ // A declaration is *applicable* on a target if one of
+ // its conjunctions of tags is a subset of the target's.
+ //
+ // One declaration is *better* than another on a target
+ // if it is applicable and its tags are a superset
+ // of the other's.
+
+ auto newLevel = getTargetSpecialiationLevel(newVal, targetName);
+ auto oldLevel = getTargetSpecialiationLevel(oldVal, targetName);
+ if(newLevel != oldLevel)
+ return UInt(newLevel) > UInt(oldLevel);
+
+ // All preceding factors being equal, an `[export]` is better
+ // than an `[import]`.
+ //
+ bool newIsExport = newVal->findDecoration<IRExportDecoration>() != nullptr;
+ bool oldIsExport = oldVal->findDecoration<IRExportDecoration>() != nullptr;
+ if(newIsExport != oldIsExport)
+ return newIsExport;
+
+ // All preceding factors being equal, a definition is
+ // better than a declaration.
+ auto newIsDef = isDefinition(newVal);
+ auto oldIsDef = isDefinition(oldVal);
+ if (newIsDef != oldIsDef)
+ return newIsDef;
+
+ return false;
+}
+
+IRFunc* cloneFuncImpl(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRFunc* originalFunc,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedFunc = builder->createFunc();
+ registerClonedValue(context, clonedFunc, originalValues);
+ cloneFunctionCommon(context, clonedFunc, originalFunc);
+ return clonedFunc;
+}
+
+
+IRInst* cloneInst(
+ IRSpecContextBase* context,
+ IRBuilder* builder,
+ IRInst* originalInst,
+ IROriginalValuesForClone const& originalValues)
+{
+ switch (originalInst->op)
+ {
+ // We need to special-case any instruction that is not
+ // allocated like an ordinary `IRInst` with trailing args.
+ case kIROp_Func:
+ return cloneFuncImpl(context, builder, cast<IRFunc>(originalInst), originalValues);
+
+ case kIROp_GlobalVar:
+ return cloneGlobalVarImpl(context, builder, cast<IRGlobalVar>(originalInst), originalValues);
+
+ case kIROp_GlobalConstant:
+ return cloneGlobalConstantImpl(context, builder, cast<IRGlobalConstant>(originalInst), originalValues);
+
+ case kIROp_GlobalParam:
+ return cloneGlobalParamImpl(context, builder, cast<IRGlobalParam>(originalInst), originalValues);
+
+ case kIROp_WitnessTable:
+ return cloneWitnessTableImpl(context, builder, cast<IRWitnessTable>(originalInst), originalValues);
+
+ case kIROp_StructType:
+ return cloneStructTypeImpl(context, builder, cast<IRStructType>(originalInst), originalValues);
+
+ case kIROp_InterfaceType:
+ return cloneInterfaceTypeImpl(context, builder, cast<IRInterfaceType>(originalInst), originalValues);
+
+ case kIROp_Generic:
+ return cloneGenericImpl(context, builder, cast<IRGeneric>(originalInst), originalValues);
+
+ case kIROp_StructKey:
+ return cloneStructKeyImpl(context, builder, cast<IRStructKey>(originalInst), originalValues);
+
+ case kIROp_GlobalGenericParam:
+ return cloneGlobalGenericParamImpl(context, builder, cast<IRGlobalGenericParam>(originalInst), originalValues);
+
+ default:
+ break;
+ }
+
+ // The common case is that we just need to construct a cloned
+ // instruction with the right number of operands, intialize
+ // it, and then add it to the sequence.
+ UInt argCount = originalInst->getOperandCount();
+ IRInst* clonedInst = builder->createIntrinsicInst(
+ cloneType(context, originalInst->getFullType()),
+ originalInst->op,
+ argCount, nullptr);
+ registerClonedValue(context, clonedInst, originalValues);
+ auto oldBuilder = context->builder;
+ context->builder = builder;
+ for (UInt aa = 0; aa < argCount; ++aa)
+ {
+ IRInst* originalArg = originalInst->getOperand(aa);
+ IRInst* clonedArg = cloneValue(context, originalArg);
+ clonedInst->getOperands()[aa].init(clonedInst, clonedArg);
+ }
+ builder->addInst(clonedInst);
+ context->builder = oldBuilder;
+ cloneDecorations(context, clonedInst, originalInst);
+
+ return clonedInst;
+}
+
+IRInst* cloneGlobalValueImpl(
+ IRSpecContext* context,
+ IRInst* originalInst,
+ IROriginalValuesForClone const& originalValues)
+{
+ auto clonedValue = cloneInst(context, &context->shared->builderStorage, originalInst, originalValues);
+ clonedValue->moveToEnd();
+ return clonedValue;
+}
+
+
+ /// Clone a global value, which has the given `originalLinkage`.
+ ///
+ /// The `originalVal` is a known global IR value with that linkage, if one is available.
+ /// (It is okay for this parameter to be null).
+ ///
+IRInst* cloneGlobalValueWithLinkage(
+ IRSpecContext* context,
+ IRInst* originalVal,
+ IRLinkageDecoration* originalLinkage)
+{
+ // If the global value being cloned is already in target module, don't clone
+ // Why checking this?
+ // When specializing a generic function G (which is already in target module),
+ // where G calls a normal function F (which is already in target module),
+ // then when we are making a copy of G via cloneFuncCommom(), it will recursively clone F,
+ // however we don't want to make a duplicate of F in the target module.
+ if (originalVal->getParent() == context->getModule()->getModuleInst())
+ return originalVal;
+
+ // Check if we've already cloned this value, for the case where
+ // an original value has already been established.
+ if (originalVal)
+ {
+ if (IRInst* clonedVal = findClonedValue(context, originalVal))
+ {
+ return clonedVal;
+ }
+ }
+
+ if(!originalLinkage)
+ {
+ // If there is no mangled name, then we assume this is a local symbol,
+ // and it can't possibly have multiple declarations.
+ return cloneGlobalValueImpl(context, originalVal, IROriginalValuesForClone(originalVal));
+ }
+
+ //
+ // We will scan through all of the available declarations
+ // with the same mangled name as `originalVal` and try
+ // to pick the "best" one for our target.
+
+ auto mangledName = String(originalLinkage->getMangledName());
+ RefPtr<IRSpecSymbol> sym;
+ if( !context->getSymbols().TryGetValue(mangledName, sym) )
+ {
+ if(!originalVal)
+ return nullptr;
+
+ // This shouldn't happen!
+ SLANG_UNEXPECTED("no matching values registered");
+ UNREACHABLE_RETURN(cloneGlobalValueImpl(context, originalVal, IROriginalValuesForClone()));
+ }
+
+ // We will try to track the "best" declaration we can find.
+ //
+ // Generally, one declaration wil lbe better than another if it is
+ // more specialized for the chosen target. Otherwise, we simply favor
+ // definitions over declarations.
+ //
+ IRInst* bestVal = sym->irGlobalValue;
+ for( auto ss = sym->nextWithSameName; ss; ss = ss->nextWithSameName )
+ {
+ IRInst* newVal = ss->irGlobalValue;
+ if(isBetterForTarget(context, newVal, bestVal))
+ bestVal = newVal;
+ }
+
+ // Check if we've already cloned this value, for the case where
+ // we didn't have an original value (just a name), but we've
+ // now found a representative value.
+ if (!originalVal)
+ {
+ if (IRInst* clonedVal = findClonedValue(context, bestVal))
+ {
+ return clonedVal;
+ }
+ }
+
+ return cloneGlobalValueImpl(context, bestVal, IROriginalValuesForClone(sym));
+}
+
+// Clone a global value, where `originalVal` is one declaration/definition, but we might
+// have to consider others, in order to find the "best" version of the symbol.
+IRInst* cloneGlobalValue(IRSpecContext* context, IRInst* originalVal)
+{
+ // We are being asked to clone a particular global value, but in
+ // the IR that comes out of the front-end there could still
+ // be multiple, target-specific, declarations of any given
+ // global value, all of which share the same mangled name.
+ return cloneGlobalValueWithLinkage(
+ context,
+ originalVal,
+ originalVal->findDecoration<IRLinkageDecoration>());
+}
+
+void insertGlobalValueSymbol(
+ IRSharedSpecContext* sharedContext,
+ IRInst* gv)
+{
+ auto linkage = gv->findDecoration<IRLinkageDecoration>();
+
+ // Don't try to register a symbol for global values
+ // that don't have linkage.
+ //
+ if (!linkage)
+ return;
+
+ auto mangledName = String(linkage->getMangledName());
+
+ RefPtr<IRSpecSymbol> sym = new IRSpecSymbol();
+ sym->irGlobalValue = gv;
+
+ RefPtr<IRSpecSymbol> prev;
+ if (sharedContext->symbols.TryGetValue(mangledName, prev))
+ {
+ sym->nextWithSameName = prev->nextWithSameName;
+ prev->nextWithSameName = sym;
+ }
+ else
+ {
+ sharedContext->symbols.Add(mangledName, sym);
+ }
+}
+
+void insertGlobalValueSymbols(
+ IRSharedSpecContext* sharedContext,
+ IRModule* originalModule)
+{
+ if (!originalModule)
+ return;
+
+ for(auto ii : originalModule->getGlobalInsts())
+ {
+ insertGlobalValueSymbol(sharedContext, ii);
+ }
+}
+
+void initializeSharedSpecContext(
+ IRSharedSpecContext* sharedContext,
+ Session* session,
+ IRModule* module,
+ CodeGenTarget target)
+{
+
+ SharedIRBuilder* sharedBuilder = &sharedContext->sharedBuilderStorage;
+ sharedBuilder->module = nullptr;
+ sharedBuilder->session = session;
+
+ IRBuilder* builder = &sharedContext->builderStorage;
+ builder->sharedBuilder = sharedBuilder;
+
+ if( !module )
+ {
+ module = builder->createModule();
+ }
+
+ sharedBuilder->module = module;
+ sharedContext->module = module;
+ sharedContext->target = target;
+}
+
+struct IRSpecializationState
+{
+ ProgramLayout* programLayout;
+ CodeGenTarget target;
+ TargetRequest* targetReq;
+
+ IRModule* irModule = nullptr;
+
+ IRSharedSpecContext sharedContextStorage;
+ IRSpecContext contextStorage;
+
+ IRSpecEnv globalEnv;
+
+ IRSharedSpecContext* getSharedContext() { return &sharedContextStorage; }
+ IRSpecContext* getContext() { return &contextStorage; }
+
+ IRSpecializationState()
+ {
+ contextStorage.env = &globalEnv;
+ }
+
+ ~IRSpecializationState()
+ {
+ contextStorage = IRSpecContext();
+ sharedContextStorage = IRSharedSpecContext();
+ }
+};
+
+LinkedIR linkIR(
+ BackEndCompileRequest* compileRequest,
+ EntryPoint* entryPoint,
+ ProgramLayout* programLayout,
+ CodeGenTarget target,
+ TargetRequest* targetReq)
+{
+ auto sink = compileRequest->getSink();
+
+ IRSpecializationState stateStorage;
+ auto state = &stateStorage;
+
+ state->programLayout = programLayout;
+ state->target = target;
+ state->targetReq = targetReq;
+
+ auto program = compileRequest->getProgram();
+
+ auto sharedContext = state->getSharedContext();
+ initializeSharedSpecContext(
+ sharedContext,
+ compileRequest->getSession(),
+ nullptr,
+ target);
+
+ state->irModule = sharedContext->module;
+
+ // We need to be able to look up IR definitions for any symbols in
+ // modules that the program depends on (transitively). To
+ // accelerate lookup, we will create a symbol table for looking
+ // up IR definitions by their mangled name.
+ //
+ auto originalProgramIRModule = program->getOrCreateIRModule(sink);
+ insertGlobalValueSymbols(sharedContext, originalProgramIRModule);
+ for (auto module : program->getModuleDependencies())
+ {
+ insertGlobalValueSymbols(sharedContext, module->getIRModule());
+ }
+
+ auto context = state->getContext();
+ context->shared = sharedContext;
+ context->builder = &sharedContext->builderStorage;
+
+ // Next, we want to optimize lookup for layout information
+ // associated with global declarations, so that we can
+ // look things up based on the IR values (using mangled names)
+ //
+ // Note: We are scanning over all the key-value pairs for
+ // entries in the global scope, to account for the fact
+ // that the "same" shader parameter could be declared in
+ // multiple translation units, and thus end up with
+ // multiple mangled names (when the unique translation
+ // unit name gets involved).
+ //
+ auto globalStructLayout = getScopeStructLayout(programLayout);
+ for(auto entry : globalStructLayout->mapVarToLayout)
+ {
+ auto mangledName = getMangledName(entry.Key);
+ auto globalVarLayout = entry.Value;
+ context->globalVarLayouts.AddIfNotExists(mangledName, globalVarLayout);
+ }
+
+ context->builder->setInsertInto(context->getModule()->getModuleInst());
+
+ // for now, clone all unreferenced witness tables
+ //
+ // TODO: This step should *not* be needed with the current IR
+ // specialization approach, so we should consider removing it.
+ //
+ for (auto sym :context->getSymbols())
+ {
+ if (sym.Value->irGlobalValue->op == kIROp_WitnessTable)
+ cloneGlobalValue(context, (IRWitnessTable*)sym.Value->irGlobalValue);
+ }
+
+ auto entryPointLayout = findEntryPointLayout(programLayout, entryPoint);
+
+ // Next, we make sure to clone the global value for
+ // the entry point function itself, and rely on
+ // this step to recursively copy over anything else
+ // it might reference.
+ auto irEntryPoint = specializeIRForEntryPoint(context, entryPoint, entryPointLayout);
+
+ // HACK: right now the bindings for global generic parameters are coming in
+ // as part of the original IR module, and we need to make sure these get
+ // copied over, even if they aren't referenced.
+ //
+ for(auto inst : originalProgramIRModule->getGlobalInsts())
+ {
+ auto bindInst = as<IRBindGlobalGenericParam>(inst);
+ if(!bindInst)
+ continue;
+
+ cloneValue(context, bindInst);
+ }
+
+ for(auto inst : originalProgramIRModule->getGlobalInsts())
+ {
+ if(inst->op != kIROp_BindGlobalExistentialSlots)
+ continue;
+
+ cloneValue(context, inst);
+ }
+
+ // HACK: we need to ensure that any tagged union types
+ // in the IR module have layout information copied over to them.
+ //
+ // Note that we do this *after* cloning the `bindGlobalGenericParam`
+ // instructions, since we expected the tagged union type(s) to
+ // be referenced by them.
+ //
+ for( auto taggedUnionTypeLayout : entryPointLayout->taggedUnionTypeLayouts )
+ {
+ auto taggedUnionType = taggedUnionTypeLayout->getType();
+ auto mangledName = getMangledTypeName(taggedUnionType);
+
+ RefPtr<IRSpecSymbol> sym;
+ if(!context->getSymbols().TryGetValue(mangledName, sym))
+ continue;
+
+ IRInst* clonedType = findClonedValue(context, sym->irGlobalValue);
+ if(!clonedType)
+ continue;
+
+ context->builder->addLayoutDecoration(clonedType, taggedUnionTypeLayout);
+ }
+
+ // TODO: *technically* we should consider the case where
+ // we have global variables with initializers, since
+ // these should get run whether or not the entry point
+ // references them.
+
+ // Now that we've cloned the entry point and everything
+ // it refers to, we can package up the data we return
+ // to the caller.
+ //
+ LinkedIR linkedIR;
+ linkedIR.module = state->irModule;
+ linkedIR.entryPoint = irEntryPoint;
+ return linkedIR;
+}
+
+
+
+} // namespace Slang