@@ -28,11 +28,13 @@ namespace Slang
28
28
return false ;
29
29
}
30
30
31
- // Casting to IRUndefined is currently vacuous
32
- // (e.g. any IRInst can be cast to IRUndefined)
33
- static bool isUndefinedValue (IRInst* inst)
31
+ static bool isUninitializedValue (IRInst* inst)
34
32
{
35
- return (inst->m_op == kIROp_undefined );
33
+ // Also consider var since it does not
34
+ // automatically mean it will be initialized
35
+ // (at least not as the user may have intended)
36
+ return (inst->m_op == kIROp_undefined )
37
+ || (inst->m_op == kIROp_Var );
36
38
}
37
39
38
40
static bool isUndefinedParam (IRParam* param)
@@ -98,31 +100,56 @@ namespace Slang
98
100
return false ;
99
101
}
100
102
101
- static bool canIgnoreType (IRType* type )
103
+ static IRInst* resolveSpecialization (IRSpecialize* spec )
102
104
{
105
+ IRInst* base = spec->getBase ();
106
+ IRGeneric* generic = as<IRGeneric>(base);
107
+ return findInnerMostGenericReturnVal (generic);
108
+ }
109
+
110
+ // The `upper` field contains the struct that the type is
111
+ // is contained in. It is used to check for empty structs.
112
+ static bool canIgnoreType (IRType* type, IRType* upper)
113
+ {
114
+ // In case specialization returns a function instead
115
+ if (!type)
116
+ return true ;
117
+
103
118
if (as<IRVoidType>(type))
104
119
return true ;
105
120
106
121
// For structs, ignore if its empty
107
- if (as<IRStructType>(type))
108
- return (type->getFirstChild () == nullptr );
122
+ if (auto str = as<IRStructType>(type))
123
+ {
124
+ int count = 0 ;
125
+ for (auto field : str->getFields ())
126
+ {
127
+ IRType* ftype = field->getFieldType ();
128
+ count += !canIgnoreType (ftype, type);
129
+ }
130
+
131
+ return (count == 0 );
132
+ }
109
133
110
134
// Nothing to initialize for a pure interface
111
135
if (as<IRInterfaceType>(type))
112
136
return true ;
113
137
114
138
// For pointers, check the value type (primarily for globals)
115
139
if (auto ptr = as<IRPtrType>(type))
116
- return canIgnoreType (ptr->getValueType ());
140
+ {
141
+ // Avoid the recursive step if its a
142
+ // recursive structure like a linked list
143
+ IRType* ptype = ptr->getValueType ();
144
+ return (ptype != upper) && canIgnoreType (ptype, upper);
145
+ }
117
146
118
147
// In the case of specializations, check returned type
119
148
if (auto spec = as<IRSpecialize>(type))
120
149
{
121
- IRInst* base = spec->getBase ();
122
- IRGeneric* generic = as<IRGeneric>(base);
123
- IRInst* inner = findInnerMostGenericReturnVal (generic);
150
+ IRInst* inner = resolveSpecialization (spec);
124
151
IRType* innerType = as<IRType>(inner);
125
- return canIgnoreType (innerType);
152
+ return canIgnoreType (innerType, upper );
126
153
}
127
154
128
155
return false ;
@@ -146,8 +173,54 @@ namespace Slang
146
173
147
174
return addresses;
148
175
}
176
+
177
+ static void checkCallUsage (List<IRInst*>& stores, List<IRInst*>& loads, IRCall* call, IRInst* inst)
178
+ {
179
+ IRInst* callee = call->getCallee ();
180
+
181
+ // Resolve the actual function
182
+ IRFunc* ftn = nullptr ;
183
+ IRFuncType* ftype = nullptr ;
184
+ if (auto spec = as<IRSpecialize>(callee))
185
+ ftn = as<IRFunc>(resolveSpecialization (spec));
186
+ else if (auto fwd = as<IRForwardDifferentiate>(callee))
187
+ ftn = as<IRFunc>(fwd->getBaseFn ());
188
+ else if (auto rev = as<IRBackwardDifferentiate>(callee))
189
+ ftn = as<IRFunc>(rev->getBaseFn ());
190
+ else if (auto wit = as<IRLookupWitnessMethod>(callee))
191
+ ftype = as<IRFuncType>(callee->getFullType ());
192
+ else
193
+ ftn = as<IRFunc>(callee);
194
+
195
+ // Find the argument index so we can fetch the type
196
+ int index = 0 ;
197
+
198
+ auto args = call->getArgsList ();
199
+ for (int i = 0 ; i < args.getCount (); i++)
200
+ {
201
+ if (args[i] == inst)
202
+ {
203
+ index = i;
204
+ break ;
205
+ }
206
+ }
207
+
208
+ if (ftn)
209
+ ftype = as<IRFuncType>(ftn->getFullType ());
210
+
211
+ if (!ftype)
212
+ return ;
213
+
214
+ // Consider it as a store if its passed
215
+ // as an out/inout/ref parameter
216
+ IRType* type = ftype->getParamType (index );
217
+ if (as<IROutType>(type) || as<IRInOutType>(type) || as<IRRefType>(type))
218
+ stores.add (call);
219
+ else
220
+ loads.add (call);
221
+ }
149
222
150
- static void collectLoadStore (List<IRInst*>& stores, List<IRInst*>& loads, IRInst* user)
223
+ static void collectLoadStore (List<IRInst*>& stores, List<IRInst*>& loads, IRInst* user, IRInst* inst )
151
224
{
152
225
// Meta intrinsics (which evaluate on type) do nothing
153
226
if (isMetaOp (user))
@@ -163,13 +236,17 @@ namespace Slang
163
236
case kIROp_unconditionalBranch :
164
237
// TODO: Ignore branches for now
165
238
return ;
239
+
240
+ case kIROp_Call :
241
+ // Function calls can be either
242
+ // stores or loads depending on
243
+ // whether the callee takes it
244
+ // in as a out parameter or not
245
+ return checkCallUsage (stores, loads, as<IRCall>(user), inst);
166
246
167
247
// These instructions will store data...
168
248
case kIROp_Store :
169
249
case kIROp_SwizzledStore :
170
- // TODO: for calls, should make check that the
171
- // function is passing as an out param
172
- case kIROp_Call :
173
250
case kIROp_SPIRVAsm :
174
251
case kIROp_GenericAsm :
175
252
// For now assume that __intrinsic_asm blocks will do the right thing...
@@ -187,6 +264,11 @@ namespace Slang
187
264
// For specializing generic structs
188
265
stores.add (user);
189
266
break ;
267
+
268
+ // Miscellaenous cases
269
+ case kIROp_ManagedPtrAttach :
270
+ stores.add (user);
271
+ break ;
190
272
191
273
// ... and the rest will load/use them
192
274
default :
@@ -225,7 +307,7 @@ namespace Slang
225
307
for (auto use = alias->firstUse ; use; use = use->nextUse )
226
308
{
227
309
IRInst* user = use->getUser ();
228
- collectLoadStore (stores, loads, user);
310
+ collectLoadStore (stores, loads, user, alias );
229
311
}
230
312
}
231
313
@@ -257,7 +339,7 @@ namespace Slang
257
339
for (auto use = alias->firstUse ; use; use = use->nextUse )
258
340
{
259
341
IRInst* user = use->getUser ();
260
- collectLoadStore (stores, loads, user);
342
+ collectLoadStore (stores, loads, user, alias );
261
343
}
262
344
}
263
345
@@ -297,11 +379,11 @@ namespace Slang
297
379
// Check ordinary instructions
298
380
for (auto inst = firstBlock->getFirstInst (); inst; inst = inst->getNextInst ())
299
381
{
300
- if (!isUndefinedValue (inst))
382
+ if (!isUninitializedValue (inst))
301
383
continue ;
302
384
303
385
IRType* type = inst->getFullType ();
304
- if (canIgnoreType (type))
386
+ if (canIgnoreType (type, nullptr ))
305
387
continue ;
306
388
307
389
auto loads = getUnresolvedVariableLoads (reachability, inst);
@@ -317,7 +399,7 @@ namespace Slang
317
399
static void checkUninitializedGlobals (IRGlobalVar* variable, DiagnosticSink* sink)
318
400
{
319
401
IRType* type = variable->getFullType ();
320
- if (canIgnoreType (type))
402
+ if (canIgnoreType (type, nullptr ))
321
403
return ;
322
404
323
405
// Check for semantic decorations
@@ -331,7 +413,7 @@ namespace Slang
331
413
if (as<IRBlock>(inst))
332
414
return ;
333
415
}
334
-
416
+
335
417
auto addresses = getAliasableInstructions (variable);
336
418
337
419
List<IRInst*> stores;
@@ -342,12 +424,14 @@ namespace Slang
342
424
for (auto use = alias->firstUse ; use; use = use->nextUse )
343
425
{
344
426
IRInst* user = use->getUser ();
345
- collectLoadStore (stores, loads, user);
427
+ collectLoadStore (stores, loads, user, alias );
346
428
347
429
// Disregard if there is at least one store,
348
430
// since we cannot tell what the control flow is
349
431
if (stores.getCount ())
350
432
return ;
433
+
434
+ // TODO: see if we can do better here (another kind of reachability check?)
351
435
}
352
436
}
353
437
0 commit comments