summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-diagnostic-defs.h1
-rw-r--r--source/slang/slang-ir-reachability.cpp39
-rw-r--r--source/slang/slang-ir-reachability.h59
-rw-r--r--source/slang/slang-ir-ssa-register-allocate.cpp82
-rw-r--r--source/slang/slang-ir-use-uninitialized-out-param.cpp134
-rw-r--r--source/slang/slang-ir-use-uninitialized-out-param.h12
-rw-r--r--source/slang/slang-lower-to-ir.cpp4
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.