#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);
};
}
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())
continue;
if (I.getType() != Type::getInt32Ty(Ctx))
continue;
if (isa<ConstantInt>(I.getOperand(1)))
continue;
ShiftInsts.push_back(cast<BinaryOperator>(&I));
}
for (auto *I : ShiftInsts) {
expand(I);
}
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);
BasicBlock *BB = BI->getParent();
Function *F = BB->getParent();
BasicBlock *EndBB = BB->splitBasicBlock(BI, "shift.done");
BasicBlock *LoopBB = BasicBlock::Create(Ctx, "shift.loop", F, EndBB);
Builder.SetInsertPoint(&BB->back());
Value *ShiftAmount = Builder.CreateTrunc(BI->getOperand(1), Int8Ty);
Value *Cmp1 = Builder.CreateICmpEQ(ShiftAmount, Int8Zero);
Builder.CreateCondBr(Cmp1, EndBB, LoopBB);
BB->back().eraseFromParent();
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);
Value *ShiftAmountSub =
Builder.CreateSub(ShiftAmountPHI, ConstantInt::get(Int8Ty, 1));
ShiftAmountPHI->addIncoming(ShiftAmountSub, LoopBB);
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);
Value *Cmp2 = Builder.CreateICmpEQ(ShiftAmountSub, Int8Zero);
Builder.CreateCondBr(Cmp2, EndBB, LoopBB);
Builder.SetInsertPoint(BI);
PHINode *Result = Builder.CreatePHI(Int32Ty, 2);
Result->addIncoming(BI->getOperand(0), BB);
Result->addIncoming(ValueShifted, LoopBB);
BI->replaceAllUsesWith(Result);
BI->eraseFromParent();
}