summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-ir-validate.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-validate.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-validate.cpp')
-rw-r--r--source/slang/slang-ir-validate.cpp207
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);
+ }
+}