//===----------------------------------------------------------------------===//
//
/// \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.
+/// Expand non-8-bit and non-16-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.
//
//===----------------------------------------------------------------------===//
if (!I.isShift())
// Only expand shift instructions (shl, lshr, ashr).
continue;
- if (I.getType() != Type::getInt32Ty(Ctx))
- // Only expand plain i32 types.
+ if (I.getType() == Type::getInt8Ty(Ctx) || I.getType() == Type::getInt16Ty(Ctx))
+ // Only expand non-8-bit and non-16-bit shifts, since those are expanded
+ // directly during isel.
continue;
if (isa<ConstantInt>(I.getOperand(1)))
// Only expand when the shift amount is not known.
void AVRShiftExpand::expand(BinaryOperator *BI) {
auto &Ctx = BI->getContext();
IRBuilder<> Builder(BI);
- Type *Int32Ty = Type::getInt32Ty(Ctx);
+ Type *InputTy = cast<Instruction>(BI)->getType();
Type *Int8Ty = Type::getInt8Ty(Ctx);
Value *Int8Zero = ConstantInt::get(Int8Ty, 0);
Builder.SetInsertPoint(LoopBB);
PHINode *ShiftAmountPHI = Builder.CreatePHI(Int8Ty, 2);
ShiftAmountPHI->addIncoming(ShiftAmount, BB);
- PHINode *ValuePHI = Builder.CreatePHI(Int32Ty, 2);
+ PHINode *ValuePHI = Builder.CreatePHI(InputTy, 2);
ValuePHI->addIncoming(BI->getOperand(0), BB);
// Subtract the shift amount by one, as we're shifting one this loop
Value *ValueShifted;
switch (BI->getOpcode()) {
case Instruction::Shl:
- ValueShifted = Builder.CreateShl(ValuePHI, ConstantInt::get(Int32Ty, 1));
+ ValueShifted = Builder.CreateShl(ValuePHI, ConstantInt::get(InputTy, 1));
break;
case Instruction::LShr:
- ValueShifted = Builder.CreateLShr(ValuePHI, ConstantInt::get(Int32Ty, 1));
+ ValueShifted = Builder.CreateLShr(ValuePHI, ConstantInt::get(InputTy, 1));
break;
case Instruction::AShr:
- ValueShifted = Builder.CreateAShr(ValuePHI, ConstantInt::get(Int32Ty, 1));
+ ValueShifted = Builder.CreateAShr(ValuePHI, ConstantInt::get(InputTy, 1));
break;
default:
llvm_unreachable("asked to expand an instruction that is not a shift");
// 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);
+ PHINode *Result = Builder.CreatePHI(InputTy, 2);
Result->addIncoming(BI->getOperand(0), BB);
Result->addIncoming(ValueShifted, LoopBB);
target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
target triple = "avr"
-define i32 @shl(i32 %value, i32 %amount) addrspace(1) {
-; CHECK-LABEL: @shl(
+define i16 @shl16(i16 %value, i16 %amount) addrspace(1) {
+; CHECK-LABEL: @shl16(
+; CHECK-NEXT: [[RESULT:%.*]] = shl i16 [[VALUE:%.*]], [[AMOUNT:%.*]]
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %result = shl i16 %value, %amount
+ ret i16 %result
+}
+
+define i32 @shl32(i32 %value, i32 %amount) addrspace(1) {
+; CHECK-LABEL: @shl32(
; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[AMOUNT:%.*]] to i8
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0
; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]]
ret i32 %result
}
-define i32 @lshr(i32 %value, i32 %amount) addrspace(1) {
-; CHECK-LABEL: @lshr(
+define i40 @shl40(i40 %value, i40 %amount) addrspace(1) {
+; CHECK-LABEL: @shl40(
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i40 [[AMOUNT:%.*]] to i8
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]]
+; CHECK: shift.loop:
+; CHECK-NEXT: [[TMP3:%.*]] = phi i8 [ [[TMP1]], [[TMP0:%.*]] ], [ [[TMP5:%.*]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: [[TMP4:%.*]] = phi i40 [ [[VALUE:%.*]], [[TMP0]] ], [ [[TMP6:%.*]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: [[TMP5]] = sub i8 [[TMP3]], 1
+; CHECK-NEXT: [[TMP6]] = shl i40 [[TMP4]], 1
+; CHECK-NEXT: [[TMP7:%.*]] = icmp eq i8 [[TMP5]], 0
+; CHECK-NEXT: br i1 [[TMP7]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]]
+; CHECK: shift.done:
+; CHECK-NEXT: [[TMP8:%.*]] = phi i40 [ [[VALUE]], [[TMP0]] ], [ [[TMP6]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: ret i40 [[TMP8]]
+;
+ %result = shl i40 %value, %amount
+ ret i40 %result
+}
+
+; ------------------------------------------------------------------------------
+
+define i16 @lshr16(i16 %value, i16 %amount) addrspace(1) {
+; CHECK-LABEL: @lshr16(
+; CHECK-NEXT: [[RESULT:%.*]] = lshr i16 [[VALUE:%.*]], [[AMOUNT:%.*]]
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %result = lshr i16 %value, %amount
+ ret i16 %result
+}
+
+define i32 @lshr32(i32 %value, i32 %amount) addrspace(1) {
+; CHECK-LABEL: @lshr32(
; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[AMOUNT:%.*]] to i8
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0
; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]]
ret i32 %result
}
-define i32 @ashr(i32 %0, i32 %1) addrspace(1) {
-; CHECK-LABEL: @ashr(
-; CHECK-NEXT: [[TMP3:%.*]] = trunc i32 [[TMP1:%.*]] to i8
-; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i8 [[TMP3]], 0
-; CHECK-NEXT: br i1 [[TMP4]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]]
+define i40 @lshr40(i40 %value, i40 %amount) addrspace(1) {
+; CHECK-LABEL: @lshr40(
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i40 [[AMOUNT:%.*]] to i8
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]]
; CHECK: shift.loop:
-; CHECK-NEXT: [[TMP5:%.*]] = phi i8 [ [[TMP3]], [[TMP2:%.*]] ], [ [[TMP7:%.*]], [[SHIFT_LOOP]] ]
-; CHECK-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP0:%.*]], [[TMP2]] ], [ [[TMP8:%.*]], [[SHIFT_LOOP]] ]
-; CHECK-NEXT: [[TMP7]] = sub i8 [[TMP5]], 1
-; CHECK-NEXT: [[TMP8]] = ashr i32 [[TMP6]], 1
-; CHECK-NEXT: [[TMP9:%.*]] = icmp eq i8 [[TMP7]], 0
-; CHECK-NEXT: br i1 [[TMP9]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]]
+; CHECK-NEXT: [[TMP3:%.*]] = phi i8 [ [[TMP1]], [[TMP0:%.*]] ], [ [[TMP5:%.*]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: [[TMP4:%.*]] = phi i40 [ [[VALUE:%.*]], [[TMP0]] ], [ [[TMP6:%.*]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: [[TMP5]] = sub i8 [[TMP3]], 1
+; CHECK-NEXT: [[TMP6]] = lshr i40 [[TMP4]], 1
+; CHECK-NEXT: [[TMP7:%.*]] = icmp eq i8 [[TMP5]], 0
+; CHECK-NEXT: br i1 [[TMP7]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]]
; CHECK: shift.done:
-; CHECK-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP0]], [[TMP2]] ], [ [[TMP8]], [[SHIFT_LOOP]] ]
-; CHECK-NEXT: ret i32 [[TMP10]]
+; CHECK-NEXT: [[TMP8:%.*]] = phi i40 [ [[VALUE]], [[TMP0]] ], [ [[TMP6]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: ret i40 [[TMP8]]
;
- %3 = ashr i32 %0, %1
- ret i32 %3
+ %result = lshr i40 %value, %amount
+ ret i40 %result
}
-; This function is not modified because it is not an i32.
-define i40 @shl40(i40 %value, i40 %amount) addrspace(1) {
-; CHECK-LABEL: @shl40(
-; CHECK-NEXT: [[RESULT:%.*]] = shl i40 [[VALUE:%.*]], [[AMOUNT:%.*]]
-; CHECK-NEXT: ret i40 [[RESULT]]
+; ------------------------------------------------------------------------------
+
+define i16 @ashr16(i16 %value, i16 %amount) addrspace(1) {
+; CHECK-LABEL: @ashr16(
+; CHECK-NEXT: [[RESULT:%.*]] = ashr i16 [[VALUE:%.*]], [[AMOUNT:%.*]]
+; CHECK-NEXT: ret i16 [[RESULT]]
;
- %result = shl i40 %value, %amount
- ret i40 %result
+ %result = ashr i16 %value, %amount
+ ret i16 %result
}
-; This function isn't either, although perhaps it should.
-define i24 @shl24(i24 %value, i24 %amount) addrspace(1) {
-; CHECK-LABEL: @shl24(
-; CHECK-NEXT: [[RESULT:%.*]] = shl i24 [[VALUE:%.*]], [[AMOUNT:%.*]]
-; CHECK-NEXT: ret i24 [[RESULT]]
+define i32 @ashr32(i32 %value, i32 %amount) addrspace(1) {
+; CHECK-LABEL: @ashr32(
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[AMOUNT:%.*]] to i8
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]]
+; CHECK: shift.loop:
+; CHECK-NEXT: [[TMP3:%.*]] = phi i8 [ [[TMP1]], [[TMP0:%.*]] ], [ [[TMP5:%.*]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: [[TMP4:%.*]] = phi i32 [ [[VALUE:%.*]], [[TMP0]] ], [ [[TMP6:%.*]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: [[TMP5]] = sub i8 [[TMP3]], 1
+; CHECK-NEXT: [[TMP6]] = ashr i32 [[TMP4]], 1
+; CHECK-NEXT: [[TMP7:%.*]] = icmp eq i8 [[TMP5]], 0
+; CHECK-NEXT: br i1 [[TMP7]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]]
+; CHECK: shift.done:
+; CHECK-NEXT: [[TMP8:%.*]] = phi i32 [ [[VALUE]], [[TMP0]] ], [ [[TMP6]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: ret i32 [[TMP8]]
+;
+ %result = ashr i32 %value, %amount
+ ret i32 %result
+}
+
+define i40 @ashr40(i40 %value, i40 %amount) addrspace(1) {
+; CHECK-LABEL: @ashr40(
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i40 [[AMOUNT:%.*]] to i8
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]]
+; CHECK: shift.loop:
+; CHECK-NEXT: [[TMP3:%.*]] = phi i8 [ [[TMP1]], [[TMP0:%.*]] ], [ [[TMP5:%.*]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: [[TMP4:%.*]] = phi i40 [ [[VALUE:%.*]], [[TMP0]] ], [ [[TMP6:%.*]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: [[TMP5]] = sub i8 [[TMP3]], 1
+; CHECK-NEXT: [[TMP6]] = ashr i40 [[TMP4]], 1
+; CHECK-NEXT: [[TMP7:%.*]] = icmp eq i8 [[TMP5]], 0
+; CHECK-NEXT: br i1 [[TMP7]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]]
+; CHECK: shift.done:
+; CHECK-NEXT: [[TMP8:%.*]] = phi i40 [ [[VALUE]], [[TMP0]] ], [ [[TMP6]], [[SHIFT_LOOP]] ]
+; CHECK-NEXT: ret i40 [[TMP8]]
;
- %result = shl i24 %value, %amount
- ret i24 %result
+ %result = ashr i40 %value, %amount
+ ret i40 %result
}