aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Target/AVR/AVRShiftExpand.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Target/AVR/AVRShiftExpand.cpp')
-rw-r--r--llvm/lib/Target/AVR/AVRShiftExpand.cpp147
1 files changed, 147 insertions, 0 deletions
diff --git a/llvm/lib/Target/AVR/AVRShiftExpand.cpp b/llvm/lib/Target/AVR/AVRShiftExpand.cpp
new file mode 100644
index 000000000000..b7dcd860467d
--- /dev/null
+++ b/llvm/lib/Target/AVR/AVRShiftExpand.cpp
@@ -0,0 +1,147 @@
+//===- 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();
+}