Enzyme main
Loading...
Searching...
No Matches
InstructionBatcher.cpp
Go to the documentation of this file.
1//===- InstructionBatcher.cpp
2//--------------------------------------------------===//
3//
4// Enzyme Project
5//
6// Part of the Enzyme Project, under the Apache License v2.0 with LLVM
7// Exceptions. See https://llvm.org/LICENSE.txt for license information.
8// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
9//
10// If using this code in an academic setting, please cite the following:
11// @incollection{enzymeNeurips,
12// title = {Instead of Rewriting Foreign Code for Machine Learning,
13// Automatically Synthesize Fast Gradients},
14// author = {Moses, William S. and Churavy, Valentin},
15// booktitle = {Advances in Neural Information Processing Systems 33},
16// year = {2020},
17// note = {To appear in},
18// }
19//
20//===----------------------------------------------------------------------===//
21//
22// This file contains an instruction visitor InstructionBatcher that generates
23// the batches all LLVM instructions.
24//
25//===----------------------------------------------------------------------===//
26
27#include "InstructionBatcher.h"
28
29#include "llvm/IR/InstVisitor.h"
30
31#include "llvm/ADT/ArrayRef.h"
32#include "llvm/ADT/SmallVector.h"
33
34#include "llvm/Support/Casting.h"
35
36#include "llvm/IR/Constants.h"
37#include "llvm/IR/IRBuilder.h"
38#include "llvm/IR/Value.h"
39
40#include "llvm/Transforms/Utils/BasicBlockUtils.h"
41#include "llvm/Transforms/Utils/Cloning.h"
42#include "llvm/Transforms/Utils/ValueMapper.h"
43
44#include "DiffeGradientUtils.h"
45#include "GradientUtils.h"
46
47using namespace llvm;
48
50 Function *oldFunc, Function *newFunc, unsigned width,
51 ValueMap<const Value *, std::vector<Value *>> &vectorizedValues,
52 ValueToValueMapTy &originalToNewFn, SmallPtrSetImpl<Value *> &toVectorize,
53 EnzymeLogic &Logic)
54 : hasError(false), vectorizedValues(vectorizedValues),
55 originalToNewFn(originalToNewFn), toVectorize(toVectorize), width(width),
56 Logic(Logic) {}
57
58Value *InstructionBatcher::getNewOperand(unsigned int i, llvm::Value *op) {
59 if (auto meta = dyn_cast<MetadataAsValue>(op)) {
60 auto md = meta->getMetadata();
61 if (auto val = dyn_cast<ValueAsMetadata>(md))
62 return MetadataAsValue::get(
63 op->getContext(),
64 ValueAsMetadata::get(getNewOperand(i, val->getValue())));
65 }
66
67 if (isa<ConstantData>(op)) {
68 return op;
69 } else if (isa<Function>(op)) {
70 return op;
71 } else if (isa<GlobalValue>(op)) {
72 llvm::errs() << "unimplelemented GlobalValue!\n";
73 llvm_unreachable("unimplelemented GlobalValue!");
74 // TODO: !!!
75 } else if (toVectorize.count(op) != 0) {
76 auto found = vectorizedValues.find(op);
77 assert(found != vectorizedValues.end());
78 return found->second[i];
79 } else {
80 auto found = originalToNewFn.find(op);
81 assert(found != originalToNewFn.end());
82 return found->second;
83 }
84}
85
86void InstructionBatcher::visitInstruction(llvm::Instruction &inst) {
87 auto found = vectorizedValues.find(&inst);
88 assert(found != vectorizedValues.end());
89 auto placeholders = found->second;
90 Instruction *placeholder = cast<Instruction>(placeholders[0]);
91
92 for (unsigned i = 1; i < width; ++i) {
93 ValueToValueMapTy vmap;
94 Instruction *new_inst = placeholder->clone();
95 vmap[placeholder] = new_inst;
96
97 for (unsigned j = 0; j < inst.getNumOperands(); ++j) {
98 Value *op = inst.getOperand(j);
99
100 // Don't allow writing vectors to global memory, loading and splatting a
101 // global is fine though.
102 if (isa<GlobalValue>(op) && !isa<ConstantData>(op) &&
103 inst.mayWriteToMemory() && toVectorize.count(op) != 0) {
104 // TODO: handle buffer access
105 hasError = true;
106 EmitFailure("GlobalValueCannotBeVectorized", inst.getDebugLoc(), &inst,
107 "global variables have to be scalar values", inst);
108 return;
109 }
110
111 if (auto meta = dyn_cast<MetadataAsValue>(op))
112 if (!isa<ValueAsMetadata>(meta->getMetadata()))
113 continue;
114
115 Value *new_op = getNewOperand(i, op);
116 vmap[placeholder->getOperand(j)] = new_op;
117 }
118
119 if (placeholders.size() == width) {
120 // Instructions which return a value
121 Instruction *placeholder = cast<Instruction>(placeholders[i]);
122 assert(!placeholder->getType()->isVoidTy());
123
124 ReplaceInstWithInst(placeholder, new_inst);
125 vectorizedValues[&inst][i] = new_inst;
126 } else if (placeholders.size() == 1) {
127 // Instructions which don't return a value
128 assert(placeholder->getType()->isVoidTy());
129
130 Instruction *insertionPoint =
131 placeholder->getNextNode() ? placeholder->getNextNode() : placeholder;
132 IRBuilder<> Builder2(insertionPoint);
133 Builder2.SetCurrentDebugLocation(DebugLoc());
134 Builder2.Insert(new_inst);
135 vectorizedValues[&inst].push_back(new_inst);
136 } else {
137 llvm_unreachable("Unexpected number of values in mapping");
138 }
139
140 RemapInstruction(new_inst, vmap, RF_NoModuleLevelChanges);
141
142 if (!inst.getType()->isVoidTy() && inst.hasName())
143 new_inst->setName(inst.getName() + Twine(i));
144 }
145}
146
148 PHINode *placeholder = cast<PHINode>(vectorizedValues[&phi][0]);
149
150 for (unsigned i = 1; i < width; ++i) {
151 ValueToValueMapTy vmap;
152 Instruction *new_phi = placeholder->clone();
153 vmap[placeholder] = new_phi;
154
155 for (unsigned j = 0; j < phi.getNumIncomingValues(); ++j) {
156 Value *orig_block = phi.getIncomingBlock(j);
157 BasicBlock *new_block = cast<BasicBlock>(originalToNewFn[orig_block]);
158 Value *orig_val = phi.getIncomingValue(j);
159 Value *new_val = getNewOperand(i, orig_val);
160
161 vmap[placeholder->getIncomingValue(j)] = new_val;
162 vmap[new_block] = new_block;
163 }
164
165 RemapInstruction(new_phi, vmap, RF_NoModuleLevelChanges);
166 Instruction *placeholder = cast<Instruction>(vectorizedValues[&phi][i]);
167 ReplaceInstWithInst(placeholder, new_phi);
168 new_phi->setName(phi.getName());
169 vectorizedValues[&phi][i] = new_phi;
170 }
171}
172
173void InstructionBatcher::visitSwitchInst(llvm::SwitchInst &inst) {
174 // TODO: runtime check
175 hasError = true;
176 EmitFailure("SwitchConditionCannotBeVectorized", inst.getDebugLoc(), &inst,
177 "switch conditions have to be scalar values", inst);
178 return;
179}
180
181#if LLVM_VERSION_MAJOR >= 24
182void InstructionBatcher::visitCondBrInst(llvm::CondBrInst &branch) {
183 visitBranch(branch);
184}
185void InstructionBatcher::visitUncondBrInst(llvm::UncondBrInst &branch) {
186 visitBranch(branch);
187}
188void InstructionBatcher::visitBranch(llvm::Instruction &branch) {
189#else
190void InstructionBatcher::visitBranchInst(llvm::BranchInst &branch) {
191#endif
192 // TODO: runtime check
193 hasError = true;
194 EmitFailure("BranchConditionCannotBeVectorized", branch.getDebugLoc(),
195 &branch, "branch conditions have to be scalar values", branch);
196 return;
197}
198
199void InstructionBatcher::visitReturnInst(llvm::ReturnInst &ret) {
200 auto found = originalToNewFn.find(ret.getParent());
201 assert(found != originalToNewFn.end());
202 BasicBlock *nBB = dyn_cast<BasicBlock>(&*found->second);
203 IRBuilder<> Builder2 = IRBuilder<>(nBB);
204 Builder2.SetCurrentDebugLocation(DebugLoc());
205 ReturnInst *placeholder = cast<ReturnInst>(nBB->getTerminator());
206 SmallVector<Value *, 4> rets;
207
208 for (unsigned j = 0; j < ret.getNumOperands(); ++j) {
209 Value *op = ret.getOperand(j);
210 for (unsigned i = 0; i < width; ++i) {
211 Value *new_op = getNewOperand(i, op);
212 rets.push_back(new_op);
213 }
214 }
215
216 if (ret.getNumOperands() != 0) {
217#if LLVM_VERSION_MAJOR > 22
218 auto ret = Builder2.CreateAggregateRet(rets);
219#else
220 auto ret = Builder2.CreateAggregateRet(rets.data(), width);
221#endif
222 ret->setDebugLoc(placeholder->getDebugLoc());
223 placeholder->eraseFromParent();
224 }
225}
226
227void InstructionBatcher::visitCallInst(llvm::CallInst &call) {
228 auto found = vectorizedValues.find(&call);
229 assert(found != vectorizedValues.end());
230 auto placeholders = found->second;
231 Instruction *placeholder = cast<Instruction>(placeholders[0]);
232 IRBuilder<> Builder2(placeholder);
233 Builder2.SetCurrentDebugLocation(DebugLoc());
234 auto orig_func = getFunctionFromCall(&call);
235
236 bool isDefined = !orig_func->isDeclaration();
237
238 if (!isDefined)
239 return visitInstruction(call);
240
241 SmallVector<Value *, 4> args;
242 SmallVector<BATCH_TYPE, 4> arg_types;
243#if LLVM_VERSION_MAJOR >= 14
244 for (unsigned j = 0; j < call.arg_size(); ++j) {
245#else
246 for (unsigned j = 0; j < call.getNumArgOperands(); ++j) {
247#endif
248 Value *op = call.getArgOperand(j);
249
250 if (toVectorize.count(op) != 0) {
251 Type *aggTy = GradientUtils::getShadowType(op->getType(), width);
252 Value *agg = UndefValue::get(aggTy);
253 for (unsigned i = 0; i < width; i++) {
254 auto found = vectorizedValues.find(op);
255 assert(found != vectorizedValues.end());
256 Value *new_op = found->second[i];
257 Builder2.CreateInsertValue(agg, new_op, {i});
258 }
259 args.push_back(agg);
260 arg_types.push_back(BATCH_TYPE::VECTOR);
261 } else if (isa<ConstantData>(op)) {
262 args.push_back(op);
263 arg_types.push_back(BATCH_TYPE::SCALAR);
264 } else {
265 auto found = originalToNewFn.find(op);
266 assert(found != originalToNewFn.end());
267 Value *arg = found->second;
268 args.push_back(arg);
269 arg_types.push_back(BATCH_TYPE::SCALAR);
270 }
271 }
272
273 BATCH_TYPE ret_type = orig_func->getReturnType()->isVoidTy()
276
277 Function *new_func = Logic.CreateBatch(RequestContext(&call, &Builder2),
278 orig_func, width, arg_types, ret_type);
279 CallInst *new_call = Builder2.CreateCall(new_func->getFunctionType(),
280 new_func, args, call.getName());
281
282 new_call->setDebugLoc(placeholder->getDebugLoc());
283
284 if (!call.getType()->isVoidTy()) {
285 for (unsigned i = 0; i < width; ++i) {
286 Instruction *placeholder = dyn_cast<Instruction>(placeholders[i]);
287 ExtractValueInst *ret = ExtractValueInst::Create(
288 new_call, {i},
289 "unwrap" + (call.hasName() ? "." + call.getName() + Twine(i) : ""));
290 ReplaceInstWithInst(placeholder, ret);
291 vectorizedValues[&call][i] = ret;
292 }
293 } else {
294 placeholder->replaceAllUsesWith(new_call);
295 placeholder->eraseFromParent();
296 }
297}
static Operation * getFunctionFromCall(CallOpInterface iface)
BATCH_TYPE
Definition Utils.h:519
void EmitFailure(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion, Args &...args)
Definition Utils.h:347
llvm::Function * CreateBatch(RequestContext context, llvm::Function *tobatch, unsigned width, llvm::ArrayRef< BATCH_TYPE > arg_types, BATCH_TYPE ret_type)
Create a function batched in its inputs.
static llvm::Type * getShadowType(llvm::Type *ty, unsigned width)
void visitReturnInst(llvm::ReturnInst &ret)
void visitSwitchInst(llvm::SwitchInst &inst)
void visitBranchInst(llvm::BranchInst &branch)
void visitInstruction(llvm::Instruction &inst)
void visitPHINode(llvm::PHINode &phi)
InstructionBatcher(llvm::Function *oldFunc, llvm::Function *newFunc, unsigned width, llvm::ValueMap< const llvm::Value *, std::vector< llvm::Value * > > &vectorizedValues, llvm::ValueMap< const llvm::Value *, llvm::WeakTrackingVH > &originalToNewFn, llvm::SmallPtrSetImpl< llvm::Value * > &toVectorize, EnzymeLogic &Logic)
void visitCallInst(llvm::CallInst &call)