forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAVRShiftExpand.cpp
147 lines (122 loc) · 5.07 KB
/
AVRShiftExpand.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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();
}