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