summaryrefslogtreecommitdiffstats
path: root/source/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/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/ir-link.cpp')
-rw-r--r--source/slang/ir-link.cpp1361
1 files changed, 0 insertions, 1361 deletions
diff --git a/source/slang/ir-link.cpp b/source/slang/ir-link.cpp
deleted file mode 100644
index dc433663a..000000000
--- a/source/slang/ir-link.cpp
+++ /dev/null
@@ -1,1361 +0,0 @@
-// ir-link.cpp
-#include "ir-link.h"
-
-#include "ir.h"
-#include "ir-insts.h"
-#include "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