diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-reachability.cpp | 39 | ||||
| -rw-r--r-- | source/slang/slang-ir-reachability.h | 59 | ||||
| -rw-r--r-- | source/slang/slang-ir-ssa-register-allocate.cpp | 82 | ||||
| -rw-r--r-- | source/slang/slang-ir-use-uninitialized-out-param.cpp | 134 | ||||
| -rw-r--r-- | source/slang/slang-ir-use-uninitialized-out-param.h | 12 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 4 |
7 files changed, 252 insertions, 79 deletions
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index dcd376474..80ba1f55b 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -593,6 +593,7 @@ DIAGNOSTIC(40020, Error, cannotUnrollLoop, "loop does not terminate within the l DIAGNOSTIC(41000, Warning, unreachableCode, "unreachable code detected") DIAGNOSTIC(41010, Warning, missingReturn, "control flow may reach end of non-'void' function") +DIAGNOSTIC(41015, Error, usingUninitializedValue, "use of uninitialized value.") DIAGNOSTIC(41011, Error, typeDoesNotFitAnyValueSize, "type '$0' does not fit in the size required by its conforming interface.") DIAGNOSTIC(41012, Note, typeAndLimit, "sizeof($0) is $1, limit is $2") diff --git a/source/slang/slang-ir-reachability.cpp b/source/slang/slang-ir-reachability.cpp new file mode 100644 index 000000000..9d36b9450 --- /dev/null +++ b/source/slang/slang-ir-reachability.cpp @@ -0,0 +1,39 @@ +#include "slang-ir-reachability.h" + +namespace Slang +{ +// Computes whether block1 can reach block2. +// A block is considered not reachable from itself unless there is a backedge in the CFG. + +bool ReachabilityContext::computeReachability(IRBlock* block1, IRBlock* block2) +{ + workList.clear(); + reachableBlocks.Clear(); + workList.add(block1); + for (Index i = 0; i < workList.getCount(); i++) + { + auto src = workList[i]; + for (auto successor : src->getSuccessors()) + { + if (successor == block2) + return true; + if (reachableBlocks.Add(successor)) + workList.add(successor); + } + } + return false; +} + +bool ReachabilityContext::isBlockReachable(IRBlock* from, IRBlock* to) +{ + BlockPair pair; + pair.first = from; + pair.second = to; + bool result = false; + if (reachabilityResults.TryGetValue(pair, result)) + return result; + result = computeReachability(from, to); + reachabilityResults[pair] = result; + return result; +} +} diff --git a/source/slang/slang-ir-reachability.h b/source/slang/slang-ir-reachability.h new file mode 100644 index 000000000..f605abd41 --- /dev/null +++ b/source/slang/slang-ir-reachability.h @@ -0,0 +1,59 @@ +// slang-ir-reachability.h +#pragma once + +#include "slang-ir.h" + +namespace Slang +{ + +// A context for computing and caching reachability between blocks on the CFG. +struct ReachabilityContext +{ + struct BlockPair + { + IRBlock* first; + IRBlock* second; + HashCode getHashCode() + { + Hasher h; + h.hashValue(first); + h.hashValue(second); + return h.getResult(); + } + bool operator == (const BlockPair& other) + { + return first == other.first && second == other.second; + } + }; + Dictionary<BlockPair, bool> reachabilityResults; + + List<IRBlock*> workList; + HashSet<IRBlock*> reachableBlocks; + + // Computes whether block1 can reach block2. + // A block is considered not reachable from itself unless there is a backedge in the CFG. + bool computeReachability(IRBlock* block1, IRBlock* block2); + + bool isBlockReachable(IRBlock* from, IRBlock* to); + + bool isInstReachable(IRInst* inst1, IRInst* inst2) + { + if (isBlockReachable(as<IRBlock>(inst1->getParent()), as<IRBlock>(inst2->getParent()))) + return true; + + // If the parent blocks are not reachable, but inst1 and inst2 are in the same block, + // we test if inst2 appears after inst1. + if (inst1->getParent() == inst2->getParent()) + { + for (auto inst = inst1->getNextInst(); inst; inst = inst->getNextInst()) + { + if (inst == inst2) + return true; + } + } + + return false; + } +}; + +} diff --git a/source/slang/slang-ir-ssa-register-allocate.cpp b/source/slang/slang-ir-ssa-register-allocate.cpp index 2f06797fa..32ca38771 100644 --- a/source/slang/slang-ir-ssa-register-allocate.cpp +++ b/source/slang/slang-ir-ssa-register-allocate.cpp @@ -1,90 +1,14 @@ // slang-ir-ssa-register-allocate.cpp #include "slang-ir-ssa-register-allocate.h" +#include "slang-ir-reachability.h" #include "slang-ir.h" #include "slang-ir-insts.h" #include "slang-ir-dominators.h" -namespace Slang { - -// A context for computing and caching reachability between blocks on the CFG. -struct ReachabilityContext +namespace Slang { - struct BlockPair - { - IRBlock* first; - IRBlock* second; - HashCode getHashCode() - { - Hasher h; - h.hashValue(first); - h.hashValue(second); - return h.getResult(); - } - bool operator == (const BlockPair& other) - { - return first == other.first && second == other.second; - } - }; - Dictionary<BlockPair, bool> reachabilityResults; - - List<IRBlock*> workList; - HashSet<IRBlock*> reachableBlocks; - - // Computes whether block1 can reach block2. - // A block is considered not reachable from itself unless there is a backedge in the CFG. - bool computeReachability(IRBlock* block1, IRBlock* block2) - { - workList.clear(); - reachableBlocks.Clear(); - workList.add(block1); - for (Index i = 0; i < workList.getCount(); i++) - { - auto src = workList[i]; - for (auto successor : src->getSuccessors()) - { - if (successor == block2) - return true; - if (reachableBlocks.Add(successor)) - workList.add(successor); - } - } - return false; - } - - bool isBlockReachable(IRBlock* from, IRBlock* to) - { - BlockPair pair; - pair.first = from; - pair.second = to; - bool result = false; - if (reachabilityResults.TryGetValue(pair, result)) - return result; - result = computeReachability(from, to); - reachabilityResults[pair] = result; - return result; - } - - bool isInstReachable(IRInst* inst1, IRInst* inst2) - { - if (isBlockReachable(as<IRBlock>(inst1->getParent()), as<IRBlock>(inst2->getParent()))) - return true; - - // If the parent blocks are not reachable, but inst1 and inst2 are in the same block, - // we test if inst2 appears after inst1. - if (inst1->getParent() == inst2->getParent()) - { - for (auto inst = inst1->getNextInst(); inst; inst = inst->getNextInst()) - { - if (inst == inst2) - return true; - } - } - - return false; - } -}; struct RegisterAllocateContext { @@ -119,7 +43,7 @@ struct RegisterAllocateContext break; } - // If isnts have the same name, prefer to coalesce them. + // If insts have the same name, prefer to coalesce them. auto name1 = inst0->findDecoration<IRNameHintDecoration>(); auto name2 = inst1->findDecoration<IRNameHintDecoration>(); if (name1 && name2 && name1->getName() == name2->getName()) diff --git a/source/slang/slang-ir-use-uninitialized-out-param.cpp b/source/slang/slang-ir-use-uninitialized-out-param.cpp new file mode 100644 index 000000000..9818cec53 --- /dev/null +++ b/source/slang/slang-ir-use-uninitialized-out-param.cpp @@ -0,0 +1,134 @@ +#include "slang-ir-use-uninitialized-out-param.h" +#include "slang-ir-util.h" +#include "slang-ir-reachability.h" + +namespace Slang +{ + class DiagnosticSink; + struct IRModule; + + struct StoreSite + { + IRInst* storeInst; + IRInst* address; + }; + + void checkForUsingUninitializedOutParams(IRFunc* func, DiagnosticSink* sink) + { + List<IRInst*> outParams; + auto firstBlock = func->getFirstBlock(); + if (!firstBlock) + return; + + ReachabilityContext reachability; + + for (auto param : firstBlock->getParams()) + { + if (auto outType = as<IROutType>(param->getFullType())) + { + // Don't check `out Vertices<T>` or `out Indices<T>` parameters + // in mesh shaders. + // TODO: we should find a better way to represent these mesh shader + // parameters so they conform to the initialize before use convention. + // For example, we can use a `OutputVetices` and `OutputIndices` type + // to represent an output, like `OutputPatch` in domain shader. + // For now, we just skip the check for these parameters. + switch (outType->getValueType()->getOp()) + { + case kIROp_VerticesType: + case kIROp_IndicesType: + case kIROp_PrimitivesType: + continue; + default: + break; + } + } + else + { + continue; + } + List<IRInst*> addresses; + addresses.add(param); + List<StoreSite> stores; + // Collect all sub-addresses from the param. + for (Index i = 0; i < addresses.getCount(); i++) + { + auto addr = addresses[i]; + for (auto use = addr->firstUse; use; use = use->nextUse) + { + switch (use->getUser()->getOp()) + { + case kIROp_GetElementPtr: + case kIROp_FieldAddress: + addresses.add(use->getUser()); + break; + case kIROp_Store: + // If we see a store of this address, add it to stores set. + if (use == use->getUser()->getOperands()) + stores.add(StoreSite{ use->getUser(), addr }); + break; + case kIROp_Call: + // If we see a call using this address, treat it as a store. + stores.add(StoreSite{ use->getUser(), addr }); + break; + } + } + } + // Check all address loads. + List<IRLoad*> loads; + for (auto addr : addresses) + { + for (auto use = addr->firstUse; use; use = use->nextUse) + { + if (auto load = as<IRLoad>(use->getUser())) + loads.add(load); + } + } + + for (auto store : stores) + { + // Remove insts from `loads` that is reachable from the store. + for (Index i = 0; i < loads.getCount();) + { + auto load = loads[i]; + if (!canAddressesPotentiallyAlias(func, store.address, loads[i]->getPtr())) + continue; + if (reachability.isInstReachable(store.storeInst, load)) + { + loads.fastRemoveAt(i); + } + else + { + i++; + } + } + } + // If there are any loads left, it means they are using uninitialized out params. + for (auto load : loads) + { + sink->diagnose(load, Diagnostics::usingUninitializedValue); + } + } + } + + void checkForUsingUninitializedOutParams( + IRModule* module, + DiagnosticSink* sink) + { + for (auto inst : module->getGlobalInsts()) + { + if (auto func = as<IRFunc>(inst)) + { + checkForUsingUninitializedOutParams(func, sink); + } + else if (auto generic = as<IRGeneric>(inst)) + { + auto retVal = findGenericReturnVal(generic); + if (auto funcVal = as<IRFunc>(retVal)) + { + checkForUsingUninitializedOutParams(funcVal, sink); + } + } + } + } +} diff --git a/source/slang/slang-ir-use-uninitialized-out-param.h b/source/slang/slang-ir-use-uninitialized-out-param.h new file mode 100644 index 000000000..fd090c4f9 --- /dev/null +++ b/source/slang/slang-ir-use-uninitialized-out-param.h @@ -0,0 +1,12 @@ +// slang-ir-use-uninitialized-out-param.h +#pragma once + +namespace Slang +{ + class DiagnosticSink; + struct IRModule; + + void checkForUsingUninitializedOutParams( + IRModule* module, + DiagnosticSink* sink); +} diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 1fba0e2f8..8cceaff02 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -26,6 +26,7 @@ #include "slang-ir-clone.h" #include "slang-ir-lower-error-handling.h" #include "slang-ir-obfuscate-loc.h" +#include "slang-ir-use-uninitialized-out-param.h" #include "slang-mangle.h" #include "slang-type-layout.h" @@ -9608,6 +9609,9 @@ RefPtr<IRModule> generateIRForTranslationUnit( // TODO: give error messages if any `undefined` or // `unreachable` instructions remain. + // Check for using uninitialized out parameters. + checkForUsingUninitializedOutParams(module, compileRequest->getSink()); + checkForMissingReturns(module, compileRequest->getSink()); // Check for invalid differentiable function body. |
