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