summaryrefslogtreecommitdiffstats
path: root/source/slang/ir-bind-existentials.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-bind-existentials.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-bind-existentials.cpp')
-rw-r--r--source/slang/ir-bind-existentials.cpp352
1 files changed, 0 insertions, 352 deletions
diff --git a/source/slang/ir-bind-existentials.cpp b/source/slang/ir-bind-existentials.cpp
deleted file mode 100644
index f0c02dd67..000000000
--- a/source/slang/ir-bind-existentials.cpp
+++ /dev/null
@@ -1,352 +0,0 @@
-// ir-bind-existentials.cpp
-#include "ir-bind-existentials.h"
-
-#include "ir.h"
-#include "ir-insts.h"
-
-namespace Slang
-{
-
-// The code that comes out of the linking step will have instructions added
-// that indicate how parameters with existential (interface) types are supposed
-// to be specialized to concrete types.
-//
-// If there are any global existential-type parameters there should be a
-// `bindGlobalExistentialSlots(...)` instruction at module scope.
-//
-// For each entry point with entry-point existential parameters, there should
-// be a `[bindExistentialSlots(...)]` decoration attached to the entry
-// point itself.
-//
-// In each case, the operands of the instruction should be a sequence of
-// pairs. The number of pairs should match the number of existential "slots"
-// at global or entry-point scope. Each pair should comprise a type `T`
-// to plug into the slot, and a witness table `w` for the conformance of
-// `T` to the interface type in that slot.
-//
-// In the simplest case, if we have a global shader parameter of interface
-// type:
-//
-// IFoo p;
-//
-// Then this will lower to the IR as:
-//
-// global_param p : IFoo;
-//
-// And if the user tries to specialie `p` to type `Bar`, and a witness
-// table `bar_is_ifoo`, we've have:
-//
-// bindGlobalExistentialSlots(Bar, bar_is_ifoo);
-//
-// The goal of this pass is to replace the parameter of interface type
-// with one of concrete type:
-//
-// global_param p_new : Bar;
-//
-// and replace any reference to the old `p` parameter with
-// a `makeExistential(p_new, bar_is_ifoo)`. That preserves the
-// fact that a reference to `p` is conceptually of type `IFoo`,
-// but allows downstream optimization passes to start specializing
-// code based on the concrete knowledge that the value "backing"
-// the parameter is actaully of type `Bar`.
-
-// As is typically for IR passes, we will encapsulate all the
-// logic in a `struct` type.
-//
-struct BindExistentialSlots
-{
- IRModule* module = nullptr;
- DiagnosticSink* sink = nullptr;
-
- void processModule()
- {
- // We will start by dealing with the global existential slots.
- processGlobalExistentialSlots();
-
- // Then we will process the per-entry-point existential slots.
- processEntryPointExistentialSlots();
- }
-
- void processGlobalExistentialSlots()
- {
- // If there are any global existential slots, we will expect
- // to find a `bindGlobalExistentialSlots` instruction at module scope.
- //
- // We will start out by finding that instruction, if it exists.
- //
- IRInst* bindGlobalExistentialSlotsInst = nullptr;
- for( auto inst : module->getGlobalInsts() )
- {
- if( inst->op == kIROp_BindGlobalExistentialSlots )
- {
- bindGlobalExistentialSlotsInst = inst;
- break;
- }
- }
-
- // Now we will start looking for global shader parameters that make
- // use of existential slots (we can determine this from their
- // layout).
- //
- for( auto inst : module->getGlobalInsts() )
- {
- // We only care about global shader parameters.
- //
- auto globalParam = as<IRGlobalParam>(inst);
- if(!globalParam)
- continue;
-
- // We will delegate to a subroutine for the meat
- // of the work, since much of it can be shared
- // with the case for entry-point existential
- // parameters.
- //
- processParameter(globalParam, bindGlobalExistentialSlotsInst);
- }
-
- // Once we are done looping over global shader parameters,
- // all of the relevant information from the
- // `bindGlobalExistentialSlots` instruction will have
- // been moved to the parameters themselves, so we
- // can eliminate the binding instruction.
- //
- if( bindGlobalExistentialSlotsInst )
- {
- bindGlobalExistentialSlotsInst->removeAndDeallocate();
- }
- }
-
- void processEntryPointExistentialSlots()
- {
- // The overall flow for the entry-point case is similar
- // to the global case.
- //
- // We start by iterating over all the functions at
- // global scope and look for entry points.
- //
- for( auto inst : module->getGlobalInsts() )
- {
- auto func = as<IRFunc>(inst);
- if(!func)
- continue;
-
- if(!func->findDecorationImpl(kIROp_EntryPointDecoration))
- continue;
-
- // We then process each entry point we find.
- //
- processEntryPointExistentialSlots(func);
- }
- }
-
- void processEntryPointExistentialSlots(IRFunc* func)
- {
- // When looking at a single `func`, we need
- // to find the `[bindExistentialSlots(...)]` decoration,
- // if it has one.
- //
- auto bindEntryPointExistentialSlotsInst = func->findDecorationImpl(kIROp_BindExistentialSlotsDecoration);
-
- // We then need to process each of the entry-point
- // parameters just like we did for global parameters.
- //
- for( auto param : func->getParams() )
- {
- processParameter(param, bindEntryPointExistentialSlotsInst);
- }
-
- // TODO: We would need to consider what to do if
- // we had an existential return type for `func`.
- //
- // In general, it probably doesn't make sense to
- // have existential types in varying input/output
- // at all, so the front-end should probably be
- // validating that.
-
- // Once we've processed all the parameters, the information
- // in the `[bindExistentialSlots(...)]` decoration is
- // no longer needed, and we can remove it.
- //
- if( bindEntryPointExistentialSlotsInst )
- {
- bindEntryPointExistentialSlotsInst->removeAndDeallocate();
- }
- }
-
- // When processing a single parameter we need to have access
- // to the corresponding instruction that will bind its slots.
- //
- // We don't care whether we have a `global_param` and a
- // `bindGlobalExistentialSlots` instruction, or an entry-point
- // function `param` and a `[bindExistentialSlots(...)]`
- // decoration; both use the same subroutine.
- //
- void processParameter(
- IRInst* param,
- IRInst* bindSlotsInst)
- {
- // We expect all shader parameters to have layout information,
- // but to be defensive we will skip any that don't.
- //
- auto layoutDecoration = param->findDecoration<IRLayoutDecoration>();
- if(!layoutDecoration)
- return;
- auto varLayout = as<VarLayout>(layoutDecoration->getLayout());
- if(!varLayout)
- return;
-
- // We only care about parameters that are associated
- // with one or more existential slots.
- //
- auto resInfo = varLayout->FindResourceInfo(LayoutResourceKind::ExistentialTypeParam);
- if(!resInfo)
- return;
-
- // We will use the layout information on the variable to
- // find out the stating slot, and the information on
- // the type to find out the number of slots.
- //
- UInt firstSlot = resInfo->index;
- UInt slotCount = 0;
- if(auto typeResInfo = varLayout->getTypeLayout()->FindResourceInfo(LayoutResourceKind::ExistentialTypeParam))
- slotCount = UInt(typeResInfo->count.getFiniteValue());
-
- // At this point we know that the parameter consumes
- // some number of slots, so it would be an error
- // if we don't have an instruction to bind the slots.
- //
- if( !bindSlotsInst )
- {
- // Note: This error is considered an internal error because
- // we should be detecting and diagnosing this problem before
- // we make it to back-end code generation.
- //
- sink->diagnose(param->sourceLoc, Diagnostics::missingExistentialBindingsForParameter);
- return;
- }
-
- // Each existential slot corresponds to *two* arguments
- // on the binding instruction: one for the type, and
- // another for the witness table.
- //
- // We will check to make sure we have enough operands to cover
- // this parameter.
- //
- UInt bindOperandCount = bindSlotsInst->getOperandCount();
- if( 2*(firstSlot + slotCount) > bindOperandCount )
- {
- sink->diagnose(param->sourceLoc, Diagnostics::missingExistentialBindingsForParameter);
- return;
- }
- //
- // If there are enough operands, then we will offset to
- // get to the starting point for the current parameter,
- // keeping in mind that each slot accounts for two
- // operands.
- //
- auto operandsForInst = bindSlotsInst->getOperands() + firstSlot;
-
- // Once we've found the operands that are relevent to
- // the slots used by `param`, we will defer to a routine
- // that replaces the type of `param` based on the
- // information in the slots.
- //
- replaceTypeUsingExistentialSlots(
- param,
- slotCount,
- operandsForInst);
- }
-
- void replaceTypeUsingExistentialSlots(
- IRInst* inst,
- UInt slotCount,
- IRUse const* slotArgs)
- {
- SLANG_UNUSED(slotCount);
-
- // We are going to alter the type of the
- // given `inst` based on information in
- // the `slotArgs`.
-
- auto fullType = inst->getFullType();
-
- SharedIRBuilder sharedBuilder;
- sharedBuilder.session = module->getSession();
- sharedBuilder.module = module;
-
- IRBuilder builder;
- builder.sharedBuilder = &sharedBuilder;
-
- // Every argument that is filling an existential
- // type param/slot comprises both a type and
- // a witness table, so the total number of operands
- // is twice the number of slots we are filling.
- //
- UInt slotOperandCount = slotCount*2;
- List<IRInst*> slotOperands;
- for(UInt ii = 0; ii < slotOperandCount; ++ii)
- slotOperands.add(slotArgs[ii].get());
-
- // We are going to create a proxy type that represents
- // the results of plugging all the information
- // from the existential slots into the original type.
- //
- auto newType = builder.getBindExistentialsType(
- fullType,
- slotOperandCount,
- slotOperands.getBuffer());
-
- // We will replace the type of the original parameter
- // with the new proxy type.
- //
- builder.setDataType(inst, newType);
-
- // Next we want to replace all uses of `inst` (which
- // expect a value of its old type) with a fresh
- // `wrapExistential(...)` instruction that refers to
- // `inst` with its new type.
- //
- // Note: we make a copy of the list of uses for `inst`
- // before going through and replacing them, because
- // during the replacement we make *more* uses of `inst`,
- // as an operand to the `makeExistential` instructions.
- // We only want to replace the old uses, and not the
- // new ones we'll be making.
- //
- List<IRUse*> usesToReplace;
- for(auto use = inst->firstUse; use; use = use->nextUse )
- usesToReplace.add(use);
-
- // Now we can loop over our list of uses and replace each.
- //
- for(auto use : usesToReplace)
- {
- // First we emit a `makeExisential` right before the
- // use site.
- //
- builder.setInsertBefore(use->getUser());
- auto newVal = builder.emitWrapExistential(
- fullType,
- inst,
- slotOperandCount,
- slotOperands.getBuffer());
-
- // Second we make the use site point at the new
- // value instead.
- //
- use->set(newVal);
- }
- }
-};
-
-void bindExistentialSlots(
- IRModule* module,
- DiagnosticSink* sink)
-{
- BindExistentialSlots context;
- context.module = module;
- context.sink = sink;
- context.processModule();
-}
-
-}