@@ -83,12 +83,36 @@ template <class C, class E> size_t getIndex(C *Container, E *FindElement) {
8383 return Idx;
8484}
8585
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>;
9193
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.
92116class YkIRWriter {
93117private:
94118 Module &M;
@@ -139,18 +163,10 @@ class YkIRWriter {
139163 OutStreamer.emitSizeT (constantIndex (C));
140164 }
141165
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);
152168 OutStreamer.emitInt8 (OperandKind::LocalVariable);
153- OutStreamer.emitSizeT (BlockIdx );
169+ OutStreamer.emitSizeT (BBIdx );
154170 OutStreamer.emitSizeT (InstIdx);
155171 }
156172
@@ -167,29 +183,45 @@ class YkIRWriter {
167183 serialiseString (toString (V));
168184 }
169185
170- void serialiseOperand (Instruction *Parent, Value *V) {
186+ void serialiseOperand (Instruction *Parent, ValueLoweringMap &VLMap,
187+ Value *V) {
171188 if (llvm::Constant *C = dyn_cast<llvm::Constant>(V)) {
172189 serialiseConstantOperand (Parent, C);
173190 } else if (Instruction *I = dyn_cast<Instruction>(V)) {
174191 // If an instruction defines the operand, it's a local variable.
175- serialiseLocalVariableOperand (I);
192+ serialiseLocalVariableOperand (I, VLMap );
176193 } else {
177194 serialiseUnimplementedOperand (V);
178195 }
179196 }
180197
181198 // / Does a naiave serialisation of an LLVM instruction by iterating over its
182199 // / 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) {
184202 OutStreamer.emitSizeT (typeIndex (I->getType ()));
185203 serialiseOpcode (Opc);
186204 OutStreamer.emitInt32 (I->getNumOperands ());
187205 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};
189210 }
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 ; \
190223 }
191224
192- void serialiseInst (Instruction *I) {
193225 GENERIC_INST_SERIALISE (I, LoadInst, Load)
194226 GENERIC_INST_SERIALISE (I, StoreInst, Store)
195227 GENERIC_INST_SERIALISE (I, AllocaInst, Alloca)
@@ -202,27 +234,37 @@ class YkIRWriter {
202234
203235 // GENERIC_INST_SERIALISE does an early return upon a match, so if we get
204236 // here then the instruction wasn't handled.
205- serialiseUnimplementedInstruction (I);
237+ serialiseUnimplementedInstruction (I, VLMap, BBIdx, InstIdx );
206238 }
207239
208240 // An unimplemented instruction is lowered to an instruction with one
209241 // 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) {
211245 // opcode:
212246 serialiseOpcode (UnimplementedInstruction);
213247 // num_operands:
214248 OutStreamer.emitInt32 (1 );
215249 // problem instruction:
216250 serialiseUnimplementedOperand (I);
251+
252+ if (!I->getType ()->isVoidTy ()) {
253+ VLMap[I] = {BBIdx, InstIdx};
254+ }
255+ InstIdx++;
217256 }
218257
219- void serialiseBlock (BasicBlock &BB) {
258+ void serialiseBlock (BasicBlock &BB, ValueLoweringMap &VLMap,
259+ unsigned &BBIdx) {
220260 // num_instrs:
221261 OutStreamer.emitSizeT (BB.size ());
222262 // instrs:
263+ unsigned InstIdx = 0 ;
223264 for (Instruction &I : BB) {
224- serialiseInst (&I);
265+ serialiseInst (&I, VLMap, BBIdx, InstIdx );
225266 }
267+ BBIdx++;
226268 }
227269
228270 void serialiseFunc (Function &F) {
@@ -231,8 +273,10 @@ class YkIRWriter {
231273 // num_blocks:
232274 OutStreamer.emitSizeT (F.size ());
233275 // blocks:
276+ unsigned BBIdx = 0 ;
277+ ValueLoweringMap VLMap;
234278 for (BasicBlock &BB : F) {
235- serialiseBlock (BB);
279+ serialiseBlock (BB, VLMap, BBIdx );
236280 }
237281 }
238282
0 commit comments