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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
|
// ir-clone.cpp
#include "ir-clone.h"
#include "ir.h"
#include "ir-insts.h"
namespace Slang
{
IRInst* lookUp(IRCloneEnv* env, IRInst* oldVal)
{
for( auto ee = env; ee; ee = ee->parent )
{
IRInst* newVal = nullptr;
if(ee->mapOldValToNew.TryGetValue(oldVal, newVal))
return newVal;
}
return nullptr;
}
IRInst* findCloneForOperand(
IRCloneEnv* env,
IRInst* oldOperand)
{
if(!oldOperand) return nullptr;
// If there is a registered replacement for
// the existing operand, then use it.
//
if( IRInst* newVal = lookUp(env, oldOperand) )
return newVal;
// Otherwise, we assume that the caller wants
// to default to using existing values wherever
// an explicit replacement hasn't been registered.
//
// This is, notably, the right default whenever
// `oldOperand` is a global value or constant
// and our cloned code will sit in the same
// module as the original.
//
// TODO: We could make this a customization point
// down the road, if we ever had a case where
// we want to clone things with a different policy.
//
return oldOperand;
}
IRInst* cloneInstAndOperands(
IRCloneEnv* env,
IRBuilder* builder,
IRInst* oldInst)
{
SLANG_ASSERT(env);
SLANG_ASSERT(builder);
SLANG_ASSERT(oldInst);
// This logic will not handle any instructions
// with special-case data attached, but that only
// applies to `IRConstant`s at this point, and those
// should only appear at the global scope rather than
// in function bodies.
//
// TODO: It would be easy enough to extend this logic
// to handle constants gracefully, if it ever comes up.
//
SLANG_ASSERT(!as<IRConstant>(oldInst));
// We start by mapping the type of the orignal instruction
// to its replacement value, if any.
//
auto oldType = oldInst->getFullType();
auto newType = (IRType*) findCloneForOperand(env, oldType);
// Next we will create an empty shell of the instruction,
// with space for the operands, but no actual operand
// values attached.
//
UInt operandCount = oldInst->getOperandCount();
auto newInst = builder->emitIntrinsicInst(
newType,
oldInst->op,
operandCount,
nullptr);
// Finally we will iterate over the operands of `oldInst`
// to find their replacements and install them as
// the operands of `newInst`.
//
for(UInt ii = 0; ii < operandCount; ++ii)
{
auto oldOperand = oldInst->getOperand(ii);
auto newOperand = findCloneForOperand(env, oldOperand);
newInst->getOperands()[ii].init(newInst, newOperand);
}
return newInst;
}
// The complexity of the second phase of cloning (the
// one that deals with decorations and children) comes
// from the fact that it needs to sequence the two phases
// of cloning for any child instructions. We will do this
// by performing the first phase of cloning, and building
// up a list of children that require the second phase of processing.
// Each entry in that list will be a pair of an old instruction
// and its new clone.
//
struct IRCloningOldNewPair
{
IRInst* oldInst;
IRInst* newInst;
};
// We will use an internal variant of `cloneInstDecorationsAndChildren`
// that modifies the provided `env` as it goes as the main
// workhorse, since we need to make sure that instructions in
// earlier blocks are visible to those in other, later, blocks
// when cloning a function, so that strict scoping along the
// lines of the nesting of instructions isn't sufficient.
//
static void _cloneInstDecorationsAndChildren(
IRCloneEnv* env,
SharedIRBuilder* sharedBuilder,
IRInst* oldInst,
IRInst* newInst)
{
SLANG_ASSERT(env);
SLANG_ASSERT(sharedBuilder);
SLANG_ASSERT(oldInst);
SLANG_ASSERT(newInst);
// We will set up an IR builder that inserts
// into the new parent instruction.
//
IRBuilder builderStorage;
auto builder = &builderStorage;
builder->sharedBuilder = sharedBuilder;
builder->setInsertInto(newInst);
// When applying the first phase of cloning to
// children, we will keep track of those that
// require the second phase.
//
List<IRCloningOldNewPair> pairs;
for( auto oldChild : oldInst->getDecorationsAndChildren() )
{
// As a very subtle special case, if one of the children
// of our `oldInst` already has a registered replacement,
// then we don't want to clone it (not least because
// the `Dictionary::Add` method would give us an error
// when we try to insert a new value for the same key).
//
// This arises for entries in `mapOldValToNew` that were
// seeded before cloning begain (e.g., function
// parameters that are to be replaced).
//
if(lookUp(env, oldChild))
continue;
// Now we can perform the first phase of cloning
// on the child, and register it in our map from
// old to new values.
//
auto newChild = cloneInstAndOperands(env, builder, oldChild);
env->mapOldValToNew.Add(oldChild, newChild);
// If and only if the old child had decorations
// or children, we will register it into our
// list for processing in the second phase.
//
if( oldChild->getFirstDecorationOrChild() )
{
IRCloningOldNewPair pair;
pair.oldInst = oldChild;
pair.newInst = newChild;
pairs.add(pair);
}
}
// Once we have done first-phase processing for
// all child instructions, we scan through those
// in the list that required second-phase processing,
// and clone their decorations and/or children recursively.
//
for( auto pair : pairs )
{
auto oldChild = pair.oldInst;
auto newChild = pair.newInst;
_cloneInstDecorationsAndChildren(env, sharedBuilder, oldChild, newChild);
}
}
// The public version of `cloneInstDecorationsAndChildren` is then
// just a wrapper over the internal one that sets up a temporary
// environment to use for the cloning process, so that we do
// not leave any lasting changes in the user-provided `env`.
//
void cloneInstDecorationsAndChildren(
IRCloneEnv* env,
SharedIRBuilder* sharedBuilder,
IRInst* oldInst,
IRInst* newInst)
{
SLANG_ASSERT(sharedBuilder);
SLANG_ASSERT(oldInst);
SLANG_ASSERT(newInst);
IRCloneEnv subEnvStorage;
auto subEnv = &subEnvStorage;
subEnv->parent = env;
_cloneInstDecorationsAndChildren(subEnv, sharedBuilder, oldInst, newInst);
}
// The convenience function `cloneInst` just sequences the
// operations that have already been defined.
//
IRInst* cloneInst(
IRCloneEnv* env,
IRBuilder* builder,
IRInst* oldInst)
{
SLANG_ASSERT(env);
SLANG_ASSERT(builder);
SLANG_ASSERT(oldInst);
auto newInst = cloneInstAndOperands(
env, builder, oldInst);
env->mapOldValToNew.Add(oldInst, newInst);
cloneInstDecorationsAndChildren(
env, builder->sharedBuilder, oldInst, newInst);
return newInst;
}
void cloneDecoration(
IRDecoration* oldDecoration,
IRInst* newParent,
IRModule* module)
{
SharedIRBuilder sharedBuilder;
sharedBuilder.module = module;
IRBuilder builder;
builder.sharedBuilder = &sharedBuilder;
if(auto first = newParent->getFirstDecorationOrChild())
builder.setInsertBefore(first);
else
builder.setInsertInto(newParent);
IRCloneEnv env;
cloneInst(&env, &builder, oldDecoration);
}
void cloneDecoration(
IRDecoration* oldDecoration,
IRInst* newParent)
{
cloneDecoration(
oldDecoration,
newParent,
newParent->getModule());
}
bool IRSimpleSpecializationKey::operator==(IRSimpleSpecializationKey const& other) const
{
auto valCount = vals.getCount();
if(valCount != other.vals.getCount()) return false;
for( Index ii = 0; ii < valCount; ++ii )
{
if(vals[ii] != other.vals[ii]) return false;
}
return true;
}
int IRSimpleSpecializationKey::GetHashCode() const
{
auto valCount = vals.getCount();
int hash = Slang::GetHashCode(valCount);
for( Index ii = 0; ii < valCount; ++ii )
{
hash = combineHash(hash, Slang::GetHashCode(vals[ii]));
}
return hash;
}
} // namespace Slang
|