1*fe6060f1SDimitry Andric //===- AVRShift.cpp - Shift Expansion Pass --------------------------------===// 2*fe6060f1SDimitry Andric // 3*fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*fe6060f1SDimitry Andric // 7*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8*fe6060f1SDimitry Andric // 9*fe6060f1SDimitry Andric /// \file 10*fe6060f1SDimitry Andric /// Expand 32-bit shift instructions (shl, lshr, ashr) to inline loops, just 11*fe6060f1SDimitry Andric /// like avr-gcc. This must be done in IR because otherwise the type legalizer 12*fe6060f1SDimitry Andric /// will turn 32-bit shifts into (non-existing) library calls such as __ashlsi3. 13*fe6060f1SDimitry Andric // 14*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 15*fe6060f1SDimitry Andric 16*fe6060f1SDimitry Andric #include "AVR.h" 17*fe6060f1SDimitry Andric #include "llvm/IR/IRBuilder.h" 18*fe6060f1SDimitry Andric #include "llvm/IR/InstIterator.h" 19*fe6060f1SDimitry Andric 20*fe6060f1SDimitry Andric using namespace llvm; 21*fe6060f1SDimitry Andric 22*fe6060f1SDimitry Andric namespace { 23*fe6060f1SDimitry Andric 24*fe6060f1SDimitry Andric class AVRShiftExpand : public FunctionPass { 25*fe6060f1SDimitry Andric public: 26*fe6060f1SDimitry Andric static char ID; 27*fe6060f1SDimitry Andric 28*fe6060f1SDimitry Andric AVRShiftExpand() : FunctionPass(ID) {} 29*fe6060f1SDimitry Andric 30*fe6060f1SDimitry Andric bool runOnFunction(Function &F) override; 31*fe6060f1SDimitry Andric 32*fe6060f1SDimitry Andric StringRef getPassName() const override { return "AVR Shift Expansion"; } 33*fe6060f1SDimitry Andric 34*fe6060f1SDimitry Andric private: 35*fe6060f1SDimitry Andric void expand(BinaryOperator *BI); 36*fe6060f1SDimitry Andric }; 37*fe6060f1SDimitry Andric 38*fe6060f1SDimitry Andric } // end of anonymous namespace 39*fe6060f1SDimitry Andric 40*fe6060f1SDimitry Andric char AVRShiftExpand::ID = 0; 41*fe6060f1SDimitry Andric 42*fe6060f1SDimitry Andric INITIALIZE_PASS(AVRShiftExpand, "avr-shift-expand", "AVR Shift Expansion", 43*fe6060f1SDimitry Andric false, false) 44*fe6060f1SDimitry Andric 45*fe6060f1SDimitry Andric Pass *llvm::createAVRShiftExpandPass() { return new AVRShiftExpand(); } 46*fe6060f1SDimitry Andric 47*fe6060f1SDimitry Andric bool AVRShiftExpand::runOnFunction(Function &F) { 48*fe6060f1SDimitry Andric SmallVector<BinaryOperator *, 1> ShiftInsts; 49*fe6060f1SDimitry Andric auto &Ctx = F.getContext(); 50*fe6060f1SDimitry Andric for (Instruction &I : instructions(F)) { 51*fe6060f1SDimitry Andric if (!I.isShift()) 52*fe6060f1SDimitry Andric // Only expand shift instructions (shl, lshr, ashr). 53*fe6060f1SDimitry Andric continue; 54*fe6060f1SDimitry Andric if (I.getType() != Type::getInt32Ty(Ctx)) 55*fe6060f1SDimitry Andric // Only expand plain i32 types. 56*fe6060f1SDimitry Andric continue; 57*fe6060f1SDimitry Andric if (isa<ConstantInt>(I.getOperand(1))) 58*fe6060f1SDimitry Andric // Only expand when the shift amount is not known. 59*fe6060f1SDimitry Andric // Known shift amounts are (currently) better expanded inline. 60*fe6060f1SDimitry Andric continue; 61*fe6060f1SDimitry Andric ShiftInsts.push_back(cast<BinaryOperator>(&I)); 62*fe6060f1SDimitry Andric } 63*fe6060f1SDimitry Andric 64*fe6060f1SDimitry Andric // The expanding itself needs to be done separately as expand() will remove 65*fe6060f1SDimitry Andric // these instructions. Removing instructions while iterating over a basic 66*fe6060f1SDimitry Andric // block is not a great idea. 67*fe6060f1SDimitry Andric for (auto *I : ShiftInsts) { 68*fe6060f1SDimitry Andric expand(I); 69*fe6060f1SDimitry Andric } 70*fe6060f1SDimitry Andric 71*fe6060f1SDimitry Andric // Return whether this function expanded any shift instructions. 72*fe6060f1SDimitry Andric return ShiftInsts.size() > 0; 73*fe6060f1SDimitry Andric } 74*fe6060f1SDimitry Andric 75*fe6060f1SDimitry Andric void AVRShiftExpand::expand(BinaryOperator *BI) { 76*fe6060f1SDimitry Andric auto &Ctx = BI->getContext(); 77*fe6060f1SDimitry Andric IRBuilder<> Builder(BI); 78*fe6060f1SDimitry Andric Type *Int32Ty = Type::getInt32Ty(Ctx); 79*fe6060f1SDimitry Andric Type *Int8Ty = Type::getInt8Ty(Ctx); 80*fe6060f1SDimitry Andric Value *Int8Zero = ConstantInt::get(Int8Ty, 0); 81*fe6060f1SDimitry Andric 82*fe6060f1SDimitry Andric // Split the current basic block at the point of the existing shift 83*fe6060f1SDimitry Andric // instruction and insert a new basic block for the loop. 84*fe6060f1SDimitry Andric BasicBlock *BB = BI->getParent(); 85*fe6060f1SDimitry Andric Function *F = BB->getParent(); 86*fe6060f1SDimitry Andric BasicBlock *EndBB = BB->splitBasicBlock(BI, "shift.done"); 87*fe6060f1SDimitry Andric BasicBlock *LoopBB = BasicBlock::Create(Ctx, "shift.loop", F, EndBB); 88*fe6060f1SDimitry Andric 89*fe6060f1SDimitry Andric // Truncate the shift amount to i8, which is trivially lowered to a single 90*fe6060f1SDimitry Andric // AVR register. 91*fe6060f1SDimitry Andric Builder.SetInsertPoint(&BB->back()); 92*fe6060f1SDimitry Andric Value *ShiftAmount = Builder.CreateTrunc(BI->getOperand(1), Int8Ty); 93*fe6060f1SDimitry Andric 94*fe6060f1SDimitry Andric // Replace the unconditional branch that splitBasicBlock created with a 95*fe6060f1SDimitry Andric // conditional branch. 96*fe6060f1SDimitry Andric Value *Cmp1 = Builder.CreateICmpEQ(ShiftAmount, Int8Zero); 97*fe6060f1SDimitry Andric Builder.CreateCondBr(Cmp1, EndBB, LoopBB); 98*fe6060f1SDimitry Andric BB->back().eraseFromParent(); 99*fe6060f1SDimitry Andric 100*fe6060f1SDimitry Andric // Create the loop body starting with PHI nodes. 101*fe6060f1SDimitry Andric Builder.SetInsertPoint(LoopBB); 102*fe6060f1SDimitry Andric PHINode *ShiftAmountPHI = Builder.CreatePHI(Int8Ty, 2); 103*fe6060f1SDimitry Andric ShiftAmountPHI->addIncoming(ShiftAmount, BB); 104*fe6060f1SDimitry Andric PHINode *ValuePHI = Builder.CreatePHI(Int32Ty, 2); 105*fe6060f1SDimitry Andric ValuePHI->addIncoming(BI->getOperand(0), BB); 106*fe6060f1SDimitry Andric 107*fe6060f1SDimitry Andric // Subtract the shift amount by one, as we're shifting one this loop 108*fe6060f1SDimitry Andric // iteration. 109*fe6060f1SDimitry Andric Value *ShiftAmountSub = 110*fe6060f1SDimitry Andric Builder.CreateSub(ShiftAmountPHI, ConstantInt::get(Int8Ty, 1)); 111*fe6060f1SDimitry Andric ShiftAmountPHI->addIncoming(ShiftAmountSub, LoopBB); 112*fe6060f1SDimitry Andric 113*fe6060f1SDimitry Andric // Emit the actual shift instruction. The difference is that this shift 114*fe6060f1SDimitry Andric // instruction has a constant shift amount, which can be emitted inline 115*fe6060f1SDimitry Andric // without a library call. 116*fe6060f1SDimitry Andric Value *ValueShifted; 117*fe6060f1SDimitry Andric switch (BI->getOpcode()) { 118*fe6060f1SDimitry Andric case Instruction::Shl: 119*fe6060f1SDimitry Andric ValueShifted = Builder.CreateShl(ValuePHI, ConstantInt::get(Int32Ty, 1)); 120*fe6060f1SDimitry Andric break; 121*fe6060f1SDimitry Andric case Instruction::LShr: 122*fe6060f1SDimitry Andric ValueShifted = Builder.CreateLShr(ValuePHI, ConstantInt::get(Int32Ty, 1)); 123*fe6060f1SDimitry Andric break; 124*fe6060f1SDimitry Andric case Instruction::AShr: 125*fe6060f1SDimitry Andric ValueShifted = Builder.CreateAShr(ValuePHI, ConstantInt::get(Int32Ty, 1)); 126*fe6060f1SDimitry Andric break; 127*fe6060f1SDimitry Andric default: 128*fe6060f1SDimitry Andric llvm_unreachable("asked to expand an instruction that is not a shift"); 129*fe6060f1SDimitry Andric } 130*fe6060f1SDimitry Andric ValuePHI->addIncoming(ValueShifted, LoopBB); 131*fe6060f1SDimitry Andric 132*fe6060f1SDimitry Andric // Branch to either the loop again (if there is more to shift) or to the 133*fe6060f1SDimitry Andric // basic block after the loop (if all bits are shifted). 134*fe6060f1SDimitry Andric Value *Cmp2 = Builder.CreateICmpEQ(ShiftAmountSub, Int8Zero); 135*fe6060f1SDimitry Andric Builder.CreateCondBr(Cmp2, EndBB, LoopBB); 136*fe6060f1SDimitry Andric 137*fe6060f1SDimitry Andric // Collect the resulting value. This is necessary in the IR but won't produce 138*fe6060f1SDimitry Andric // any actual instructions. 139*fe6060f1SDimitry Andric Builder.SetInsertPoint(BI); 140*fe6060f1SDimitry Andric PHINode *Result = Builder.CreatePHI(Int32Ty, 2); 141*fe6060f1SDimitry Andric Result->addIncoming(BI->getOperand(0), BB); 142*fe6060f1SDimitry Andric Result->addIncoming(ValueShifted, LoopBB); 143*fe6060f1SDimitry Andric 144*fe6060f1SDimitry Andric // Replace the original shift instruction. 145*fe6060f1SDimitry Andric BI->replaceAllUsesWith(Result); 146*fe6060f1SDimitry Andric BI->eraseFromParent(); 147*fe6060f1SDimitry Andric } 148