diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-05-31 17:20:37 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-05-31 17:20:37 -0400 |
| commit | 6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f (patch) | |
| tree | 5a23cb47782e9e2a77762c90dd35da1005eba8d0 /source/slang/ir-dce.cpp | |
| parent | b81ff3ef968d1cc4e954b31a1812b3c391d17b02 (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-dce.cpp')
| -rw-r--r-- | source/slang/ir-dce.cpp | 325 |
1 files changed, 0 insertions, 325 deletions
diff --git a/source/slang/ir-dce.cpp b/source/slang/ir-dce.cpp deleted file mode 100644 index f1a34bedf..000000000 --- a/source/slang/ir-dce.cpp +++ /dev/null @@ -1,325 +0,0 @@ -// ir-dce.cpp -#include "ir-dce.h" - -#include "ir.h" -#include "ir-insts.h" - -namespace Slang -{ - -struct DeadCodeEliminationContext -{ - // This type implements a simple global DCE pass over - // an entire module. - // - // We start with member variables to stand in for - // the parameters that were passed to the top-level - // `eliminateDeadCode` function. - // - BackEndCompileRequest* compileRequest; - IRModule* module; - - // Our overall process is going to be to determine - // which instructions in the module are "live" - // and then eliminate anything that wasn't found to - // be live. - // - // We will track the liveness state by keeping - // a set of all instructions we have so far determined - // to be live. - // - HashSet<IRInst*> liveInsts; - - // Querying whether an instruction has been - // determined to be live is easy. - // - bool isInstLive(IRInst* inst) - { - // The only wrinkle is that we want to safeguard - // against a null instruction (there are some - // corner cases where we still construct IR - // instructions with a null type). - // - if(!inst) return false; - - return liveInsts.Contains(inst); - } - - // We are going to do an iterative analysis - // where we mark instructions we know are - // live, and then see if that can help us - // identify any other instructions that - // must also be live. - // - // For this, we will use a work list of - // instructions that have been marked - // as live, but for which we haven't - // looked at their impact on other - // instructions. - // - List<IRInst*> workList; - - // When we discover that an instruction seems - // to be live, we will add it to our set, - // and also the work list, but only if we - // haven't done so previously. - // - void markInstAsLive(IRInst* inst) - { - // Again, we safeguard against null instructions - // just in case. - // - if(!inst) return; - - if(liveInsts.Contains(inst)) - return; - liveInsts.Add(inst); - workList.add(inst); - } - - // Given the basic infrastructrure above, let's - // dive into the task of actually finding all - // the live code in a module. - // - void processModule() - { - // First of all, we know that the root module instruction - // should be considered as live, because otherwise - // we'd end up eliminating it, so that is a - // good place to start. - // - markInstAsLive(module->getModuleInst()); - - // Marking the module as live should have - // seeded our work list, so we can now start - // processing entries off of our work list - // until it goes dry. - // - while( workList.getCount() ) - { - auto inst = workList.getLast(); - workList.removeLast(); - - // At this point we know that `inst` is live, - // and we want to start considering which other - // instructions must be live because of that - // knowlege. - // - // A first easy case is that the parent (if any) - // of a live instruction had better be live, or - // else we might delete the parent, and - // the child with it. - // - markInstAsLive(inst->getParent()); - - // Next the type of a live instruction, and all - // of its operands must also be live, or else - // we won't be able to compute its value. - // - markInstAsLive(inst->getFullType()); - UInt operandCount = inst->getOperandCount(); - for( UInt ii = 0; ii < operandCount; ++ii ) - { - markInstAsLive(inst->getOperand(ii)); - } - - // Finally, we need to consider the children - // and decorations of the instruction. - // - // Note that just because an instruction is - // live doesn't mean its children must be, or - // else we'd never eliminate *anything* (we - // marked the whole module as live, and everything - // is a transitive child of the module). - // - // Decorations, in contrast, are always live if their - // parents are (because we don't want to silently drop - // decorations). It is still important to *mark* - // decorations as live, because they have operands, - // and those operands need to be marked as live. - // We will fold decorations into the same loop - // as children for simplicity. - // - // To keep the code here simple, we'll defer the - // decision of whether a child (or decoration) - // should be live when its parent is to a subroutine. - // - for( auto child : inst->getDecorationsAndChildren() ) - { - if(shouldInstBeLiveIfParentIsLive(child)) - { - // In this case, we know `inst` is live and - // its `child` should be live if its parent is, - // so the `child` must be live too. - // - markInstAsLive(child); - } - } - } - - // If our work list runs dry, that means we've reached a steady - // state where everything that is transitively relevant to - // the "outputs" of the module has been marked as live. - // - // Now we can simply walk through all of our instructions - // recursively and eliminate those that are "dead" by - // virtue of not having been found live. - // - eliminateDeadInstsRec(module->getModuleInst()); - } - - void eliminateDeadInstsRec(IRInst* inst) - { - // Given the instruction `inst` we need to eliminate - // any dead code at, or under it. - // - // The easy case is if `inst` is dead (that is, not live). - // - if( !isInstLive(inst) ) - { - // We can simply remove and deallocate `inst` because it is - // dead, and not worry about any of its descendents, - // because they must have been dead too (since we always - // mark the parent of a live instruction as live). - // - inst->removeAndDeallocate(); - } - else - { - // If `inst` is live, then we need to deal with the possibility - // that its children/decorations (or descendents in general) - // might still be dead. - // - // The biggest wrinkle is that we walk the linked list of - // children/decorations a bit carefully, using a temporary - // to hold the next node, in case we eliminate one of - // the children as we go. - // - IRInst* next = nullptr; - for( IRInst* child = inst->getFirstDecorationOrChild(); child; child = next ) - { - next = child->getNextInst(); - eliminateDeadInstsRec(child); - } - } - } - - // Now we come to the decision procedure we put off before: - // should a given `inst` be live if its parent is? - // - bool shouldInstBeLiveIfParentIsLive(IRInst* inst) - { - // The main source of confusion/complexity here is that - // we are using the same routine to decide: - // - // * Should some ordinary instruction in a basic block be kept around? - // * Should a basic block in some function be kept around? - // * Should a function/type/variable in a module be kept around? - // - // Still, there are a few basic patterns we can observe. - // First, if `inst` is an instruction that might have some effects - // when it is executed, then we should keep it around. - // - if(inst->mightHaveSideEffects()) - return true; - // - // The `mightHaveSideEffects` query is conservative, and will - // return `true` as its default mode, so once we are past that - // query we know that `inst` is either something "structural" - // (that makes up the program) rather than executable, or it - // is executable but was on a white list of things that are - // safe to eliminate. - - // Most top-level objects (functions, types, etc.) obviously - // do *not* have side effects. That creates the risk that - // we'll just go ahead and eliminate every single function/type - // in a module. There needs to be a way to identify the - // functions we want to keep around, and for right now - // that is handled with the `[keepAlive]` decoration. - // - if(inst->findDecorationImpl(kIROp_KeepAliveDecoration)) - return true; - // - // TODO: Eventually it would make sense to consider everything - // with an `[export(...)]` decoration as live, but our current - // approach to linking for back-end compilation leaves many - // linkage decorations in place that we seemingly don't need/want. - - // A basic block is an interesting case. Knowing that a function - // is live means that its entry block is live, but the liveness - // of any other blocks is determined by whether they are referenced - // by other instructions (e.g., a branch from one block to - // another). - // - if( auto block = as<IRBlock>(inst) ) - { - // To determine whether this is the first block in its - // parent function (or what-have-you) we can simply - // check if there is a previous block before it. - // - auto prevBlock = block->getPrevBlock(); - return prevBlock == nullptr; - } - - // There are a few special cases of "structural" instructions - // that we don't want to eliminate, so we'll check for those next. - // - switch( inst->op ) - { - // Function parameters obviously shouldn't get eliminated, - // even if nothing references them, and block parameters - // (phi nodes) will be considered live when their block is, - // just so that we don't have to deal with any complications - // around re-writing the relevant inter-block argument passing. - // - // TODO: A smarter DCE pass could deal with this case more - // carefully, or we could improve the interprocedural SCCP - // pass to deal with block parameters instead. - // - case kIROp_Param: - return true; - - // IR struct types and witness tables are currently kludged - // so that they have child instructions that represent their - // entries (effectively `(key,value)` pairs), and those child - // instructions are never directly referenced (e.g., an access - // to a struct field references the *key* but not the `(key,value)` - // pair that is the `IRField` instruction. - // - // TODO: at some point the IR should use a different representation - // for struct types and witness tables that does away with - // this problem. - // - case kIROp_StructField: - case kIROp_WitnessTableEntry: - return true; - - default: - break; - } - - // If none of the explicit cases above matched, then we will consider - // the instruction to not be live just because its parent is. Further - // analysis could still lead to a change in the status of `inst`, if - // an instruction that uses it as an operand is marked live. - // - return false; - } -}; - -// The top-level function for invoking the DCE pass -// is straighforward. We set up the context object -// and then defer to it for the real work. -// -void eliminateDeadCode( - BackEndCompileRequest* compileRequest, - IRModule* module) -{ - DeadCodeEliminationContext context; - context.compileRequest = compileRequest; - context.module = module; - - context.processModule(); -} - -} |
