1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
#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(func);
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:
case kIROp_SwizzledStore:
// 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:
case kIROp_SPIRVAsm:
// If we see a call using this address, treat it as a store.
stores.add(StoreSite{ use->getUser(), addr });
break;
case kIROp_SPIRVAsmOperandInst:
stores.add(StoreSite{ use->getUser()->getParent(), addr});
break;
}
}
}
// Check all address loads.
List<IRInst*> loadsAndReturns;
for (auto addr : addresses)
{
for (auto use = addr->firstUse; use; use = use->nextUse)
{
if (auto load = as<IRLoad>(use->getUser()))
loadsAndReturns.add(load);
}
}
for(const auto& b : func->getBlocks())
{
auto t = as<IRReturn>(b->getTerminator());
if (!t) continue;
loadsAndReturns.add(t);
}
for (auto store : stores)
{
// Remove insts from `loads` that is reachable from the store.
for (Index i = 0; i < loadsAndReturns.getCount();)
{
auto load = as<IRLoad>(loadsAndReturns[i]);
if (load && !canAddressesPotentiallyAlias(func, store.address, load->getPtr()))
continue;
if (reachability.isInstReachable(store.storeInst, loadsAndReturns[i]))
{
loadsAndReturns.fastRemoveAt(i);
}
else
{
i++;
}
}
}
// If there are any loads left, it means they are using uninitialized out params.
for (auto load : loadsAndReturns)
{
sink->diagnose(
load,
load->m_op == kIROp_Return
? Diagnostics::returningWithUninitializedOut
: Diagnostics::usingUninitializedValue,
param);
}
}
}
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);
}
}
}
}
}
|