| //===- AVRShift.cpp - Shift Expansion Pass --------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| /// \file | |
| /// Expand 32-bit shift instructions (shl, lshr, ashr) to inline loops, just | |
| /// like avr-gcc. This must be done in IR because otherwise the type legalizer | |
| /// will turn 32-bit shifts into (non-existing) library calls such as __ashlsi3. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "AVR.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/IR/InstIterator.h" | |
| using namespace llvm; | |
| namespace { | |
| class AVRShiftExpand : public FunctionPass { | |
| public: | |
| static char ID; | |
| AVRShiftExpand() : FunctionPass(ID) {} | |
| bool runOnFunction(Function &F) override; | |
| StringRef getPassName() const override { return "AVR Shift Expansion"; } | |
| private: | |
| void expand(BinaryOperator *BI); | |
| }; | |
| } // end of anonymous namespace | |
| char AVRShiftExpand::ID = 0; | |
| INITIALIZE_PASS(AVRShiftExpand, "avr-shift-expand", "AVR Shift Expansion", | |
| false, false) | |
| Pass *llvm::createAVRShiftExpandPass() { return new AVRShiftExpand(); } | |
| bool AVRShiftExpand::runOnFunction(Function &F) { | |
| SmallVector<BinaryOperator *, 1> ShiftInsts; | |
| auto &Ctx = F.getContext(); | |
| for (Instruction &I : instructions(F)) { | |
| if (!I.isShift()) | |
| // Only expand shift instructions (shl, lshr, ashr). | |
| continue; | |
| if (I.getType() != Type::getInt32Ty(Ctx)) | |
| // Only expand plain i32 types. | |
| continue; | |
| if (isa<ConstantInt>(I.getOperand(1))) | |
| // Only expand when the shift amount is not known. | |
| // Known shift amounts are (currently) better expanded inline. | |
| continue; | |
| ShiftInsts.push_back(cast<BinaryOperator>(&I)); | |
| } | |
| // The expanding itself needs to be done separately as expand() will remove | |
| // these instructions. Removing instructions while iterating over a basic | |
| // block is not a great idea. | |
| for (auto *I : ShiftInsts) { | |
| expand(I); | |
| } | |
| // Return whether this function expanded any shift instructions. | |
| return ShiftInsts.size() > 0; | |
| } | |
| void AVRShiftExpand::expand(BinaryOperator *BI) { | |
| auto &Ctx = BI->getContext(); | |
| IRBuilder<> Builder(BI); | |
| Type *Int32Ty = Type::getInt32Ty(Ctx); | |
| Type *Int8Ty = Type::getInt8Ty(Ctx); | |
| Value *Int8Zero = ConstantInt::get(Int8Ty, 0); | |
| // Split the current basic block at the point of the existing shift | |
| // instruction and insert a new basic block for the loop. | |
| BasicBlock *BB = BI->getParent(); | |
| Function *F = BB->getParent(); | |
| BasicBlock *EndBB = BB->splitBasicBlock(BI, "shift.done"); | |
| BasicBlock *LoopBB = BasicBlock::Create(Ctx, "shift.loop", F, EndBB); | |
| // Truncate the shift amount to i8, which is trivially lowered to a single | |
| // AVR register. | |
| Builder.SetInsertPoint(&BB->back()); | |
| Value *ShiftAmount = Builder.CreateTrunc(BI->getOperand(1), Int8Ty); | |
| // Replace the unconditional branch that splitBasicBlock created with a | |
| // conditional branch. | |
| Value *Cmp1 = Builder.CreateICmpEQ(ShiftAmount, Int8Zero); | |
| Builder.CreateCondBr(Cmp1, EndBB, LoopBB); | |
| BB->back().eraseFromParent(); | |
| // Create the loop body starting with PHI nodes. | |
| Builder.SetInsertPoint(LoopBB); | |
| PHINode *ShiftAmountPHI = Builder.CreatePHI(Int8Ty, 2); | |
| ShiftAmountPHI->addIncoming(ShiftAmount, BB); | |
| PHINode *ValuePHI = Builder.CreatePHI(Int32Ty, 2); | |
| ValuePHI->addIncoming(BI->getOperand(0), BB); | |
| // Subtract the shift amount by one, as we're shifting one this loop | |
| // iteration. | |
| Value *ShiftAmountSub = | |
| Builder.CreateSub(ShiftAmountPHI, ConstantInt::get(Int8Ty, 1)); | |
| ShiftAmountPHI->addIncoming(ShiftAmountSub, LoopBB); | |
| // Emit the actual shift instruction. The difference is that this shift | |
| // instruction has a constant shift amount, which can be emitted inline | |
| // without a library call. | |
| Value *ValueShifted; | |
| switch (BI->getOpcode()) { | |
| case Instruction::Shl: | |
| ValueShifted = Builder.CreateShl(ValuePHI, ConstantInt::get(Int32Ty, 1)); | |
| break; | |
| case Instruction::LShr: | |
| ValueShifted = Builder.CreateLShr(ValuePHI, ConstantInt::get(Int32Ty, 1)); | |
| break; | |
| case Instruction::AShr: | |
| ValueShifted = Builder.CreateAShr(ValuePHI, ConstantInt::get(Int32Ty, 1)); | |
| break; | |
| default: | |
| llvm_unreachable("asked to expand an instruction that is not a shift"); | |
| } | |
| ValuePHI->addIncoming(ValueShifted, LoopBB); | |
| // Branch to either the loop again (if there is more to shift) or to the | |
| // basic block after the loop (if all bits are shifted). | |
| Value *Cmp2 = Builder.CreateICmpEQ(ShiftAmountSub, Int8Zero); | |
| Builder.CreateCondBr(Cmp2, EndBB, LoopBB); | |
| // Collect the resulting value. This is necessary in the IR but won't produce | |
| // any actual instructions. | |
| Builder.SetInsertPoint(BI); | |
| PHINode *Result = Builder.CreatePHI(Int32Ty, 2); | |
| Result->addIncoming(BI->getOperand(0), BB); | |
| Result->addIncoming(ValueShifted, LoopBB); | |
| // Replace the original shift instruction. | |
| BI->replaceAllUsesWith(Result); | |
| BI->eraseFromParent(); | |
| } |