void BlockGenerator::copyBB(ScopStmt &Stmt, BasicBlock *BB, BasicBlock *CopyBB, ValueMapT &BBMap, ValueMapT &GlobalMap, LoopToScevMapT <S) { Builder.SetInsertPoint(CopyBB->begin()); EntryBB = &CopyBB->getParent()->getEntryBlock(); for (Instruction &Inst : *BB) copyInstruction(Stmt, &Inst, BBMap, GlobalMap, LTS); // After a basic block was copied store all scalars that escape this block // in their alloca. First the scalars that have dependences inside the SCoP, // then the ones that might escape the SCoP. generateScalarStores(Stmt, BB, BBMap, GlobalMap); const Region &R = Stmt.getParent()->getRegion(); for (Instruction &Inst : *BB) handleOutsideUsers(R, &Inst, BBMap[&Inst]); }
void VectorBlockGenerator::copyInstruction(ScopStmt &Stmt, const Instruction *Inst, ValueMapT &VectorMap, VectorValueMapT &ScalarMaps) { // Terminator instructions control the control flow. They are explicitly // expressed in the clast and do not need to be copied. if (Inst->isTerminator()) return; if (canSynthesize(Inst, &LI, &SE, &Stmt.getParent()->getRegion())) return; if (const LoadInst *Load = dyn_cast<LoadInst>(Inst)) { generateLoad(Stmt, Load, VectorMap, ScalarMaps); return; } if (hasVectorOperands(Inst, VectorMap)) { if (const StoreInst *Store = dyn_cast<StoreInst>(Inst)) { copyStore(Stmt, Store, VectorMap, ScalarMaps); return; } if (const UnaryInstruction *Unary = dyn_cast<UnaryInstruction>(Inst)) { copyUnaryInst(Stmt, Unary, VectorMap, ScalarMaps); return; } if (const BinaryOperator *Binary = dyn_cast<BinaryOperator>(Inst)) { copyBinaryInst(Stmt, Binary, VectorMap, ScalarMaps); return; } // Falltrough: We generate scalar instructions, if we don't know how to // generate vector code. } copyInstScalarized(Stmt, Inst, VectorMap, ScalarMaps); }
void BlockGenerator::generateScalarStores(ScopStmt &Stmt, BasicBlock *BB, ValueMapT &BBMap, ValueMapT &GlobalMap) { const Region &R = Stmt.getParent()->getRegion(); assert(Stmt.isBlockStmt() && BB == Stmt.getBasicBlock() && "Region statements need to use the generateScalarStores() " "function in the RegionGenerator"); // Set to remember a store to the phiops alloca of a PHINode. It is needed as // we might have multiple write accesses to the same PHI and while one is the // self write of the PHI (to the ScalarMap alloca) the other is the write to // the operand alloca (PHIOpMap). SmallPtrSet<PHINode *, 4> SeenPHIs; // Iterate over all accesses in the given statement. for (MemoryAccess *MA : Stmt) { // Skip non-scalar and read accesses. if (!MA->isScalar() || MA->isRead()) continue; Instruction *ScalarBase = cast<Instruction>(MA->getBaseAddr()); Instruction *ScalarInst = MA->getAccessInstruction(); PHINode *ScalarBasePHI = dyn_cast<PHINode>(ScalarBase); // Get the alloca node for the base instruction and the value we want to // store. In total there are 4 options: // (1) The base is no PHI, hence it is a simple scalar def-use chain. // (2) The base is a PHI, // (a) and the write is caused by an operand in the block. // (b) and it is the PHI self write (same as case (1)). // (c) (2a) and (2b) are not distinguishable. // For case (1) and (2b) we get the alloca from the scalar map and the value // we want to store is initialized with the instruction attached to the // memory access. For case (2a) we get the alloca from the PHI operand map // and the value we want to store is initialized with the incoming value for // this block. The tricky case (2c) is when both (2a) and (2b) match. This // happens if the PHI operand is in the same block as the PHI. To handle // that we choose the alloca of (2a) first and (2b) for the next write // access to that PHI (there must be 2). Value *ScalarValue = nullptr; AllocaInst *ScalarAddr = nullptr; if (!ScalarBasePHI) { // Case (1) ScalarAddr = getOrCreateAlloca(ScalarBase, ScalarMap, ".s2a"); ScalarValue = ScalarInst; } else { int PHIIdx = ScalarBasePHI->getBasicBlockIndex(BB); if (ScalarBasePHI != ScalarInst) { // Case (2a) assert(PHIIdx >= 0 && "Bad scalar write to PHI operand"); SeenPHIs.insert(ScalarBasePHI); ScalarAddr = getOrCreateAlloca(ScalarBase, PHIOpMap, ".phiops"); ScalarValue = ScalarBasePHI->getIncomingValue(PHIIdx); } else if (PHIIdx < 0) { // Case (2b) ScalarAddr = getOrCreateAlloca(ScalarBase, ScalarMap, ".s2a"); ScalarValue = ScalarInst; } else { // Case (2c) if (SeenPHIs.insert(ScalarBasePHI).second) { // First access ==> same as (2a) ScalarAddr = getOrCreateAlloca(ScalarBase, PHIOpMap, ".phiops"); ScalarValue = ScalarBasePHI->getIncomingValue(PHIIdx); } else { // Second access ==> same as (2b) ScalarAddr = getOrCreateAlloca(ScalarBase, ScalarMap, ".s2a"); ScalarValue = ScalarInst; } } } ScalarValue = getNewScalarValue(ScalarValue, R, ScalarMap, BBMap, GlobalMap); Builder.CreateStore(ScalarValue, ScalarAddr); } }
void BlockGenerator::copyInstruction(ScopStmt &Stmt, const Instruction *Inst, ValueMapT &BBMap, ValueMapT &GlobalMap, LoopToScevMapT <S) { // First check for possible scalar dependences for this instruction. generateScalarLoads(Stmt, Inst, BBMap); // Terminator instructions control the control flow. They are explicitly // expressed in the clast and do not need to be copied. if (Inst->isTerminator()) return; Loop *L = getLoopForInst(Inst); if ((Stmt.isBlockStmt() || !Stmt.getRegion()->contains(L)) && canSynthesize(Inst, &LI, &SE, &Stmt.getParent()->getRegion())) { Value *NewValue = getNewValue(Stmt, Inst, BBMap, GlobalMap, LTS, L); BBMap[Inst] = NewValue; return; } if (const LoadInst *Load = dyn_cast<LoadInst>(Inst)) { Value *NewLoad = generateScalarLoad(Stmt, Load, BBMap, GlobalMap, LTS); // Compute NewLoad before its insertion in BBMap to make the insertion // deterministic. BBMap[Load] = NewLoad; return; } if (const StoreInst *Store = dyn_cast<StoreInst>(Inst)) { Value *NewStore = generateScalarStore(Stmt, Store, BBMap, GlobalMap, LTS); // Compute NewStore before its insertion in BBMap to make the insertion // deterministic. BBMap[Store] = NewStore; return; } if (const PHINode *PHI = dyn_cast<PHINode>(Inst)) { copyPHIInstruction(Stmt, PHI, BBMap, GlobalMap, LTS); return; } // Skip some special intrinsics for which we do not adjust the semantics to // the new schedule. All others are handled like every other instruction. if (auto *IT = dyn_cast<IntrinsicInst>(Inst)) { switch (IT->getIntrinsicID()) { // Lifetime markers are ignored. case llvm::Intrinsic::lifetime_start: case llvm::Intrinsic::lifetime_end: // Invariant markers are ignored. case llvm::Intrinsic::invariant_start: case llvm::Intrinsic::invariant_end: // Some misc annotations are ignored. case llvm::Intrinsic::var_annotation: case llvm::Intrinsic::ptr_annotation: case llvm::Intrinsic::annotation: case llvm::Intrinsic::donothing: case llvm::Intrinsic::assume: case llvm::Intrinsic::expect: return; default: // Other intrinsics are copied. break; } } copyInstScalar(Stmt, Inst, BBMap, GlobalMap, LTS); }
void RegionGenerator::generateScalarStores(ScopStmt &Stmt, BasicBlock *BB, ValueMapT &BBMap, ValueMapT &GlobalMap) { const Region &R = Stmt.getParent()->getRegion(); Region *StmtR = Stmt.getRegion(); assert(StmtR && "Block statements need to use the generateScalarStores() " "function in the BlockGenerator"); BasicBlock *ExitBB = StmtR->getExit(); // For region statements three kinds of scalar stores exists: // (1) A definition used by a non-phi instruction outside the region. // (2) A phi-instruction in the region entry. // (3) A write to a phi instruction in the region exit. // The last case is the tricky one since we do not know anymore which // predecessor of the exit needs to store the operand value that doesn't // have a definition in the region. Therefore, we have to check in each // block in the region if we should store the value or not. // Iterate over all accesses in the given statement. for (MemoryAccess *MA : Stmt) { // Skip non-scalar and read accesses. if (!MA->isScalar() || MA->isRead()) continue; Instruction *ScalarBase = cast<Instruction>(MA->getBaseAddr()); Instruction *ScalarInst = MA->getAccessInstruction(); PHINode *ScalarBasePHI = dyn_cast<PHINode>(ScalarBase); Value *ScalarValue = nullptr; AllocaInst *ScalarAddr = nullptr; if (!ScalarBasePHI) { // Case (1) ScalarAddr = getOrCreateAlloca(ScalarBase, ScalarMap, ".s2a"); ScalarValue = ScalarInst; } else if (ScalarBasePHI->getParent() != ExitBB) { // Case (2) assert(ScalarBasePHI->getParent() == StmtR->getEntry() && "Bad PHI self write in non-affine region"); assert(ScalarBase == ScalarInst && "Bad PHI self write in non-affine region"); ScalarAddr = getOrCreateAlloca(ScalarBase, ScalarMap, ".s2a"); ScalarValue = ScalarInst; } else { int PHIIdx = ScalarBasePHI->getBasicBlockIndex(BB); // Skip accesses we will not handle in this basic block but in another one // in the statement region. if (PHIIdx < 0) continue; // Case (3) ScalarAddr = getOrCreateAlloca(ScalarBase, PHIOpMap, ".phiops"); ScalarValue = ScalarBasePHI->getIncomingValue(PHIIdx); } ScalarValue = getNewScalarValue(ScalarValue, R, ScalarMap, BBMap, GlobalMap); Builder.CreateStore(ScalarValue, ScalarAddr); } }