@@ -83,12 +83,36 @@ template <class C, class E> size_t getIndex(C *Container, E *FindElement) {
83
83
return Idx;
84
84
}
85
85
86
- #define GENERIC_INST_SERIALISE (LLVM_INST, LLVM_INST_TYPE, YKIR_OPCODE ) \
87
- if (isa<LLVM_INST_TYPE>(LLVM_INST)) { \
88
- serialiseInstGeneric (LLVM_INST, YKIR_OPCODE); \
89
- return ; \
90
- }
86
+ // A <BBIdx, InstrIdx> pair that Uniquely identifies an Yk IR instruction within
87
+ // a function.
88
+ using InstrLoc = std::tuple<size_t , size_t >;
89
+
90
+ // Maps an LLVM instruction that generates a value to the corresponding Yk IR
91
+ // instruction.
92
+ using ValueLoweringMap = map<Instruction *, InstrLoc>;
91
93
94
+ // The class responsible for serialising our IR into the interpreter binary.
95
+ //
96
+ // It walks over the LLVM IR, lowering each function, block, instruction, etc.
97
+ // into a Yk IR equivalent.
98
+ //
99
+ // As it does this there are some invariants that must be maintained:
100
+ //
101
+ // - The current basic block index (BBIdx) is passed down the lowering process.
102
+ // This must be incremented each time we finish a Yk IR basic block.
103
+ //
104
+ // - Similarly for instructions. Each time we finish a Yk IR instruction,
105
+ // we must increment the current instruction index (InstIdx).
106
+ //
107
+ // - When we are done lowering an LLVM instruction that generates a value, we
108
+ // must update the `VLMap` with an entry that maps the LLVM instruction to
109
+ // the final Yk IR instruction in the lowering. If the LLVM instruction
110
+ // doesn't generate a value, or the LLVM instruction lowered to exactly zero
111
+ // Yk IR instructions, then there is no need to update the `VLMap`.
112
+ //
113
+ // These invariants are required so that when we encounter a local variable as
114
+ // an operand to an LLVM instruction, we can quickly find the corresponding Yk
115
+ // IR local variable.
92
116
class YkIRWriter {
93
117
private:
94
118
Module &M;
@@ -139,18 +163,10 @@ class YkIRWriter {
139
163
OutStreamer.emitSizeT (constantIndex (C));
140
164
}
141
165
142
- void serialiseLocalVariableOperand (Instruction *I) {
143
- // For now we assume that there is a one to one relationship between LLVM
144
- // instructions and Yk IR instructions, and that the instruction
145
- // (and block) indices are the same in both IRs.
146
- BasicBlock *ParentBlock = I->getParent ();
147
- Function *ParentFunc = ParentBlock->getParent ();
148
-
149
- size_t BlockIdx = getIndex (ParentFunc, ParentBlock);
150
- size_t InstIdx = getIndex (ParentBlock, I);
151
-
166
+ void serialiseLocalVariableOperand (Instruction *I, ValueLoweringMap &VLMap) {
167
+ auto [BBIdx, InstIdx] = VLMap.at (I);
152
168
OutStreamer.emitInt8 (OperandKind::LocalVariable);
153
- OutStreamer.emitSizeT (BlockIdx );
169
+ OutStreamer.emitSizeT (BBIdx );
154
170
OutStreamer.emitSizeT (InstIdx);
155
171
}
156
172
@@ -167,29 +183,45 @@ class YkIRWriter {
167
183
serialiseString (toString (V));
168
184
}
169
185
170
- void serialiseOperand (Instruction *Parent, Value *V) {
186
+ void serialiseOperand (Instruction *Parent, ValueLoweringMap &VLMap,
187
+ Value *V) {
171
188
if (llvm::Constant *C = dyn_cast<llvm::Constant>(V)) {
172
189
serialiseConstantOperand (Parent, C);
173
190
} else if (Instruction *I = dyn_cast<Instruction>(V)) {
174
191
// If an instruction defines the operand, it's a local variable.
175
- serialiseLocalVariableOperand (I);
192
+ serialiseLocalVariableOperand (I, VLMap );
176
193
} else {
177
194
serialiseUnimplementedOperand (V);
178
195
}
179
196
}
180
197
181
198
// / Does a naiave serialisation of an LLVM instruction by iterating over its
182
199
// / operands and serialising them in turn.
183
- void serialiseInstGeneric (Instruction *I, OpCode Opc) {
200
+ void serialiseInstGeneric (Instruction *I, ValueLoweringMap &VLMap,
201
+ unsigned BBIdx, unsigned &InstIdx, OpCode Opc) {
184
202
OutStreamer.emitSizeT (typeIndex (I->getType ()));
185
203
serialiseOpcode (Opc);
186
204
OutStreamer.emitInt32 (I->getNumOperands ());
187
205
for (Value *O : I->operands ()) {
188
- serialiseOperand (I, O);
206
+ serialiseOperand (I, VLMap, O);
207
+ }
208
+ if (!I->getType ()->isVoidTy ()) {
209
+ VLMap[I] = {BBIdx, InstIdx};
189
210
}
211
+ InstIdx++;
212
+ }
213
+
214
+ void serialiseInst (Instruction *I, ValueLoweringMap &VLMap, unsigned BBIdx,
215
+ unsigned &InstIdx) {
216
+ // Macro to help dispatch to generic lowering.
217
+ //
218
+ // Note that this is unhygenic so as to make the call-sites readable.
219
+ #define GENERIC_INST_SERIALISE (LLVM_INST, LLVM_INST_TYPE, YKIR_OPCODE ) \
220
+ if (isa<LLVM_INST_TYPE>(LLVM_INST)) { \
221
+ serialiseInstGeneric (LLVM_INST, VLMap, BBIdx, InstIdx, YKIR_OPCODE); \
222
+ return ; \
190
223
}
191
224
192
- void serialiseInst (Instruction *I) {
193
225
GENERIC_INST_SERIALISE (I, LoadInst, Load)
194
226
GENERIC_INST_SERIALISE (I, StoreInst, Store)
195
227
GENERIC_INST_SERIALISE (I, AllocaInst, Alloca)
@@ -202,27 +234,37 @@ class YkIRWriter {
202
234
203
235
// GENERIC_INST_SERIALISE does an early return upon a match, so if we get
204
236
// here then the instruction wasn't handled.
205
- serialiseUnimplementedInstruction (I);
237
+ serialiseUnimplementedInstruction (I, VLMap, BBIdx, InstIdx );
206
238
}
207
239
208
240
// An unimplemented instruction is lowered to an instruction with one
209
241
// unimplemented operand containing the textual LLVM IR we couldn't handle.
210
- void serialiseUnimplementedInstruction (Instruction *I) {
242
+ void serialiseUnimplementedInstruction (Instruction *I,
243
+ ValueLoweringMap &VLMap,
244
+ unsigned BBIdx, unsigned &InstIdx) {
211
245
// opcode:
212
246
serialiseOpcode (UnimplementedInstruction);
213
247
// num_operands:
214
248
OutStreamer.emitInt32 (1 );
215
249
// problem instruction:
216
250
serialiseUnimplementedOperand (I);
251
+
252
+ if (!I->getType ()->isVoidTy ()) {
253
+ VLMap[I] = {BBIdx, InstIdx};
254
+ }
255
+ InstIdx++;
217
256
}
218
257
219
- void serialiseBlock (BasicBlock &BB) {
258
+ void serialiseBlock (BasicBlock &BB, ValueLoweringMap &VLMap,
259
+ unsigned &BBIdx) {
220
260
// num_instrs:
221
261
OutStreamer.emitSizeT (BB.size ());
222
262
// instrs:
263
+ unsigned InstIdx = 0 ;
223
264
for (Instruction &I : BB) {
224
- serialiseInst (&I);
265
+ serialiseInst (&I, VLMap, BBIdx, InstIdx );
225
266
}
267
+ BBIdx++;
226
268
}
227
269
228
270
void serialiseFunc (Function &F) {
@@ -231,8 +273,10 @@ class YkIRWriter {
231
273
// num_blocks:
232
274
OutStreamer.emitSizeT (F.size ());
233
275
// blocks:
276
+ unsigned BBIdx = 0 ;
277
+ ValueLoweringMap VLMap;
234
278
for (BasicBlock &BB : F) {
235
- serialiseBlock (BB);
279
+ serialiseBlock (BB, VLMap, BBIdx );
236
280
}
237
281
}
238
282
0 commit comments