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/slang-ir-validate.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/slang-ir-validate.cpp')
| -rw-r--r-- | source/slang/slang-ir-validate.cpp | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/source/slang/slang-ir-validate.cpp b/source/slang/slang-ir-validate.cpp new file mode 100644 index 000000000..15228200e --- /dev/null +++ b/source/slang/slang-ir-validate.cpp @@ -0,0 +1,207 @@ +// slang-ir-validate.cpp +#include "slang-ir-validate.h" + +#include "slang-ir.h" +#include "slang-ir-insts.h" + +namespace Slang +{ + struct IRValidateContext + { + // The IR module we are validating. + IRModule* module; + + // A diagnostic sink to send errors to if anything is invalid. + DiagnosticSink* sink; + + DiagnosticSink* getSink() { return sink; } + + // A set of instructions we've seen, to help confirm that + // values are defined before they are used in a given block. + HashSet<IRInst*> seenInsts; + }; + + void validateIRInst( + IRValidateContext* context, + IRInst* inst); + + void validate(IRValidateContext* context, bool condition, IRInst* inst, char const* message) + { + if (!condition) + { + context->getSink()->diagnose(inst, Diagnostics::irValidationFailed, message); + } + } + + void validateIRInstChildren( + IRValidateContext* context, + IRInst* parent) + { + IRInst* prevChild = nullptr; + for(auto child : parent->getDecorationsAndChildren() ) + { + // We need to check the integrity of the parent/next/prev links of + // all of our instructions + validate(context, child->parent == parent, child, "parent link"); + validate(context, child->prev == prevChild, child, "next/prev link"); + + // Recursively validate the instruction itself. + validateIRInst(context, child); + + // Do some extra validation around terminator instructions: + // + // * The last instruction of a block should always be a terminator + // * No other instruction should be a terminator + // + if(as<IRBlock>(parent) && (child == parent->getLastDecorationOrChild())) + { + validate(context, as<IRTerminatorInst>(child) != nullptr, child, "last instruction in block must be terminator"); + } + else + { + validate(context, !as<IRTerminatorInst>(child), child, "terminator must be last instruction in a block"); + } + + + prevChild = child; + } + } + + void validateIRInstOperand( + IRValidateContext* context, + IRInst* inst, + IRUse* operandUse) + { + // The `IRUse` for the operand had better have `inst` as its user. + validate(context, operandUse->getUser() == inst, inst, "operand user"); + + // The value we are using needs to fit into one of a few cases. + // + // * If the parent of `inst` and of `operand` is the same block, then + // we require that `operand` is defined before `inst` + // + // * If the parents of `inst` and `operand` are both blocks in the + // same functin, then the block defining `operand` must dominate + // the block defining `inst`. + // + // * Otherwise, we simply require that the parent of `operand` be + // an ancestor (transitive parent) of `inst`. + + auto instParent = inst->getParent(); + + auto operandValue = operandUse->get(); + + if( !operandValue ) + { + // A null operand should almost always be an error, but + // we currently have a few cases where this arises. + // + // TODO: plug the leaks. + return; + } + + auto operandParent = operandValue->getParent(); + + if (auto instParentBlock = as<IRBlock>(instParent)) + { + if (auto operandParentBlock = as<IRBlock>(operandParent)) + { + if (instParentBlock == operandParentBlock) + { + // If `operandValue` precedes `inst`, then we should + // have already seen it, because we scan parent instructions + // in order. + validate(context, context->seenInsts.Contains(operandValue), inst, "def must come before use in same block"); + return; + } + + auto instFunc = instParentBlock->getParent(); + auto operandFunc = operandParentBlock->getParent(); + if (instFunc == operandFunc) + { + // The two instructions are defined in different blocks of + // the same function (or another value with code). We need + // to validate that `operandParentBlock` dominates `instParentBlock`. + // + // TODO: implement this validation once we compute dominator trees. + // + // validate(context, operandParentBlock->dominates(instParentBlock), inst, "def must dominate use"); + return; + } + } + } + + // If the special cases above did not trigger, then either the two values + // are nested in the same parent, but that parent isn't a block, or they + // are nested in distinct parents, and those parents aren't both children + // of a function. + // + // In either case, we need to enforce that the parent of `operand` needs + // to be an ancestor of `inst`. + // + for (auto pp = instParent; pp; pp = pp->getParent()) + { + if (pp == operandParent) + return; + } + // + // We failed to find `operandParent` while walking the ancestors of `inst`, + // so something had gone wrong. + validate(context, false, inst, "def must be ancestor of use"); + } + + void validateIRInstOperands( + IRValidateContext* context, + IRInst* inst) + { + if(inst->getFullType()) + validateIRInstOperand(context, inst, &inst->typeUse); + + UInt operandCount = inst->getOperandCount(); + for (UInt ii = 0; ii < operandCount; ++ii) + { + validateIRInstOperand(context, inst, inst->getOperands() + ii); + } + } + + void validateIRInst( + IRValidateContext* context, + IRInst* inst) + { + // Validate that any operands of the instruction are used appropriately + validateIRInstOperands(context, inst); + context->seenInsts.Add(inst); + + // If `inst` is itself a parent instruction, then we need to recursively + // validate its children. + validateIRInstChildren(context, inst); + } + + void validateIRModule(IRModule* module, DiagnosticSink* sink) + { + IRValidateContext contextStorage; + IRValidateContext* context = &contextStorage; + context->module = module; + context->sink = sink; + + auto moduleInst = module->moduleInst; + + validate(context, moduleInst != nullptr, moduleInst, "module instruction"); + validate(context, moduleInst->parent == nullptr, moduleInst, "module instruction parent"); + validate(context, moduleInst->prev == nullptr, moduleInst, "module instruction prev"); + validate(context, moduleInst->next == nullptr, moduleInst, "module instruction next"); + + validateIRInst(context, module->moduleInst); + } + + void validateIRModuleIfEnabled( + CompileRequestBase* compileRequest, + IRModule* module) + { + if (!compileRequest->shouldValidateIR) + return; + + auto sink = compileRequest->getSink(); + validateIRModule(module, sink); + } +} |
