xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/AVR/AVRShiftExpand.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
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