| //===- 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(); |
| } |