Index: lib/Target/Mips/MipsFastISel.cpp =================================================================== --- lib/Target/Mips/MipsFastISel.cpp +++ lib/Target/Mips/MipsFastISel.cpp @@ -100,10 +100,12 @@ bool selectRet(const Instruction *I); bool selectTrunc(const Instruction *I); bool selectIntExt(const Instruction *I); + bool selectShift(const Instruction *I); // Utility helper routines. bool isTypeLegal(Type *Ty, MVT &VT); bool isTypeSupported(Type *Ty, MVT &VT); + bool isValueAvailable(const Value *V) const; bool isLoadTypeLegal(Type *Ty, MVT &VT); bool computeAddress(const Value *Obj, Address &Addr); bool computeCallAddress(const Value *V, Address &Addr); @@ -478,6 +480,16 @@ return false; } +bool MipsFastISel::isValueAvailable(const Value *V) const { + if (!isa(V)) + return true; + + const auto *I = cast(V); + if (FuncInfo.MBBMap[I->getParent()] == FuncInfo.MBB) + return true; + + return false; +} bool MipsFastISel::isLoadTypeLegal(Type *Ty, MVT &VT) { if (isTypeLegal(Ty, VT)) return true; @@ -1279,8 +1291,28 @@ if (!SrcReg) return false; - // Because the high bits are undefined, a truncate doesn't generate - // any code. + uint64_t Mask = 0; + MVT DVT = DestVT.getSimpleVT(); + switch (DVT.SimpleTy) { + default: + // Trunc i64 to i32 is handled by the target-independent fast-isel. + return false; + case MVT::i1: + Mask = 0x1; + break; + case MVT::i8: + Mask = 0xff; + break; + case MVT::i16: + Mask = 0xffff; + break; + } + if (Mask != 0) { + unsigned DestReg = createResultReg(&Mips::GPR32RegClass); + emitInst(Mips::ANDi, DestReg).addReg(SrcReg).addImm(Mask); + SrcReg = DestReg; + } + updateValueMap(I, SrcReg); return true; } @@ -1386,6 +1418,71 @@ return Success ? DestReg : 0; } +bool MipsFastISel::selectShift(const Instruction *I) { + MVT RetVT; + if (!isTypeSupported(I->getType(), RetVT)) + return false; + unsigned ResultReg = createResultReg(&Mips::GPR32RegClass); + if (!ResultReg) + return false; + unsigned Opcode = I->getOpcode(); + const Value *Op0 = I->getOperand(0); + unsigned Op0Reg = getRegForValue(Op0); + if (!Op0Reg) + return false; + // if AShr then we need to make sure the operand0 is sign extended + // + if (Opcode == Instruction::AShr) { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + if (!TempReg) + return false; + MVT Op0MVT = TLI.getValueType(Op0->getType(), true).getSimpleVT(); + if (!emitIntSExt(Op0MVT, Op0Reg, MVT::i32, TempReg)) + return false; + Op0Reg = TempReg; + } + if (const auto *C = dyn_cast(I->getOperand(1))) { + uint64_t ShiftVal = C->getZExtValue(); + switch (Opcode) { + default: + llvm_unreachable("Unexpected instruction."); + case Instruction::Shl: + Opcode = Mips::SLL; + break; + case Instruction::AShr: + Opcode = Mips::SRA; + break; + case Instruction::LShr: + Opcode = Mips::SRL; + break; + } + emitInst(Opcode, ResultReg).addReg(Op0Reg).addImm(ShiftVal); + updateValueMap(I, ResultReg); + return true; + } + + unsigned Op1Reg = getRegForValue(I->getOperand(1)); + if (!Op1Reg) + return false; + + switch (Opcode) { + default: + llvm_unreachable("Unexpected instruction."); + case Instruction::Shl: + Opcode = Mips::SLLV; + break; + case Instruction::AShr: + Opcode = Mips::SRAV; + break; + case Instruction::LShr: + Opcode = Mips::SRLV; + break; + } + emitInst(Opcode, ResultReg).addReg(Op0Reg).addReg(Op1Reg); + updateValueMap(I, ResultReg); + return true; +} + bool MipsFastISel::fastSelectInstruction(const Instruction *I) { if (!TargetSupported) return false; @@ -1396,6 +1493,10 @@ return selectLoad(I); case Instruction::Store: return selectStore(I); + case Instruction::Shl: + case Instruction::LShr: + case Instruction::AShr: + return selectShift(I); case Instruction::And: case Instruction::Or: case Instruction::Xor: Index: test/CodeGen/Mips/Fast-ISel/shftopm.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/Fast-ISel/shftopm.ll @@ -0,0 +1,216 @@ +; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32r2 < %s | FileCheck %s +; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32 < %s | FileCheck %s + + +@s1 = global i16 -89, align 2 +@s2 = global i16 4, align 2 +@us1 = global i16 -503, align 2 +@us2 = global i16 5, align 2 +@s3 = common global i16 0, align 2 +@us3 = common global i16 0, align 2 + +; Function Attrs: nounwind +define void @sll() #0 { +entry: + %0 = load i16* @s1, align 2 + %1 = load i16* @s2, align 2 + %shl = shl i16 %0, %1 + store i16 %shl, i16* @s3, align 2 +; CHECK: .ent sll +; CHECK: lui $[[REG_GPa:[0-9]+]], %hi(_gp_disp) +; CHECK-DAG: addiu $[[REG_GPb:[0-9]+]], $[[REG_GPa]], %lo(_gp_disp) +; CHECK-DAG: addu $[[REG_GP:[0-9]+]], $[[REG_GPb]], $25 +; CHECK-DAG: lw $[[S3_ADDR:[0-9]+]], %got(s3)($[[REG_GP]]) +; CHECK-DAG: lw $[[S2_ADDR:[0-9]+]], %got(s2)($[[REG_GP]]) +; CHECK-DAG: lw $[[S1_ADDR:[0-9]+]], %got(s1)($[[REG_GP]]) +; CHECK-DAG: lhu $[[S1:[0-9]+]], 0($[[S1_ADDR]]) +; CHECK-DAG: lhu $[[S2:[0-9]+]], 0($[[S2_ADDR]]) +; CHECK: sllv $[[RES:[0-9]+]], $[[S1]], $[[S2]] +; CHECK: sh $[[RES]], 0($[[S3_ADDR]]) +; CHECK: .end sll + ret void +} + +; Function Attrs: nounwind +define void @slli() #0 { +entry: + %0 = load i16* @s1, align 2 + %shl = shl i16 %0, 5 + store i16 %shl, i16* @s3, align 2 +; CHECK: .ent slli +; CHECK: lui $[[REG_GPa:[0-9]+]], %hi(_gp_disp) +; CHECK-DAG: addiu $[[REG_GPb:[0-9]+]], $[[REG_GPa]], %lo(_gp_disp) +; CHECK-DAG: addu $[[REG_GP:[0-9]+]], $[[REG_GPb]], $25 +; CHECK-DAG: lw $[[S3_ADDR:[0-9]+]], %got(s3)($[[REG_GP]]) +; CHECK-DAG: lw $[[S1_ADDR:[0-9]+]], %got(s1)($[[REG_GP]]) +; CHECK-DAG: lhu $[[S1:[0-9]+]], 0($[[S1_ADDR]]) +; CHECK: sll $[[RES:[0-9]+]], $[[S1]], 5 +; CHECK: sh $[[RES]], 0($[[S3_ADDR]]) +; CHECK: .end slli + ret void +} + +; Function Attrs: nounwind +define void @srl() #0 { +entry: + %0 = load i16* @us1, align 2 + %1 = load i16* @us2, align 2 + %shr = lshr i16 %0, %1 + store i16 %shr, i16* @us3, align 2 + ret void +; CHECK: .ent srl +; CHECK: lui $[[REG_GPa:[0-9]+]], %hi(_gp_disp) +; CHECK-DAG: addiu $[[REG_GPb:[0-9]+]], $[[REG_GPa]], %lo(_gp_disp) +; CHECK-DAG: addu $[[REG_GP:[0-9]+]], $[[REG_GPb]], $25 +; CHECK-DAG: lw $[[US3_ADDR:[0-9]+]], %got(us3)($[[REG_GP]]) +; CHECK-DAG: lw $[[US2_ADDR:[0-9]+]], %got(us2)($[[REG_GP]]) +; CHECK-DAG: lw $[[US1_ADDR:[0-9]+]], %got(us1)($[[REG_GP]]) +; CHECK-DAG: lhu $[[US1:[0-9]+]], 0($[[US1_ADDR]]) +; CHECK-DAG: lhu $[[US2:[0-9]+]], 0($[[US2_ADDR]]) +; CHECK: srlv $[[RES:[0-9]+]], $[[US1]], $[[US2]] +; CHECK: sh $[[RES]], 0($[[S3_ADDR]]) +; CHECK: .end srl +} + +; Function Attrs: nounwind +define void @srli() #0 { +entry: + %0 = load i16* @us1, align 2 + %shr = lshr i16 %0, 4 + store i16 %shr, i16* @us3, align 2 +; CHECK: .ent srli +; CHECK: lui $[[REG_GPa:[0-9]+]], %hi(_gp_disp) +; CHECK-DAG: addiu $[[REG_GPb:[0-9]+]], $[[REG_GPa]], %lo(_gp_disp) +; CHECK-DAG: addu $[[REG_GP:[0-9]+]], $[[REG_GPb]], $25 +; CHECK-DAG: lw $[[US3_ADDR:[0-9]+]], %got(us3)($[[REG_GP]]) +; CHECK-DAG: lw $[[US1_ADDR:[0-9]+]], %got(us1)($[[REG_GP]]) +; CHECK-DAG: lhu $[[US1:[0-9]+]], 0($[[US1_ADDR]]) +; CHECK: srl $[[RES:[0-9]+]], $[[US1]], 4 +; CHECK: sh $[[RES]], 0($[[S3_ADDR]]) +; CHECK: .end srli + ret void +} + +; Function Attrs: nounwind +define void @sra() #0 { +entry: + %0 = load i16* @s1, align 2 + %1 = load i16* @s2, align 2 + %shr = ashr i16 %0, %1 + store i16 %shr, i16* @s3, align 2 +; CHECK: .ent sra +; CHECK: lui $[[REG_GPa:[0-9]+]], %hi(_gp_disp) +; CHECK-DAG: addiu $[[REG_GPb:[0-9]+]], $[[REG_GPa]], %lo(_gp_disp) +; CHECK-DAG: addu $[[REG_GP:[0-9]+]], $[[REG_GPb]], $25 +; CHECK-DAG: lw $[[S3_ADDR:[0-9]+]], %got(s3)($[[REG_GP]]) +; CHECK-DAG: lw $[[S2_ADDR:[0-9]+]], %got(s2)($[[REG_GP]]) +; CHECK-DAG: lw $[[S1_ADDR:[0-9]+]], %got(s1)($[[REG_GP]]) +; CHECK-DAG: lhu $[[S1:[0-9]+]], 0($[[S1_ADDR]]) +; CHECK-DAG: lhu $[[S2:[0-9]+]], 0($[[S2_ADDR]]) +; CHECK: srav $[[RES:[0-9]+]], $[[S1]], $[[S2]] +; CHECK: sh $[[RES]], 0($[[S3_ADDR]]) +; CHECK: .end sra + ret void +} + +; Function Attrs: nounwind +define void @srai() #0 { +entry: + %0 = load i16* @s1, align 2 + %shr = ashr i16 %0, 2 + store i16 %shr, i16* @s3, align 2 +; CHECK: .ent sra +; CHECK: lui $[[REG_GPa:[0-9]+]], %hi(_gp_disp) +; CHECK-DAG: addiu $[[REG_GPb:[0-9]+]], $[[REG_GPa]], %lo(_gp_disp) +; CHECK-DAG: addu $[[REG_GP:[0-9]+]], $[[REG_GPb]], $25 +; CHECK-DAG: lw $[[S3_ADDR:[0-9]+]], %got(s3)($[[REG_GP]]) +; CHECK-DAG: lw $[[S1_ADDR:[0-9]+]], %got(s1)($[[REG_GP]]) +; CHECK-DAG: lhu $[[S1:[0-9]+]], 0($[[S1_ADDR]]) +; CHECK: sra $[[RES:[0-9]+]], $[[S1]], 2 +; CHECK: sh $[[RES]], 0($[[S3_ADDR]]) +; CHECK: .end sra + ret void +} + +; Function Attrs: nounwind +;define i32 @main() #0 { +;entry: +; %retval = alloca i32, align 4 +; store i32 0, i32* %retval +; call void @sll() +; %0 = load i16* @s3, align 2 +; %conv = sext i16 %0 to i32 +; %cmp = icmp ne i32 %conv, -1424 +; br i1 %cmp, label %if.then, label %if.end +; +;if.then: ; preds = %entry +; call void @abort() #2 +; unreachable +; +;if.end: ; preds = %entry +; call void @slli() +; %1 = load i16* @s3, align 2 +; %conv2 = sext i16 %1 to i32 +; %cmp3 = icmp ne i32 %conv2, -2848 +; br i1 %cmp3, label %if.then5, label %if.end6 +; +;if.then5: ; preds = %if.end +; call void @abort() #2 +; unreachable +; +;if.end6: ; preds = %if.end +; call void @srl() +; %2 = load i16* @us3, align 2 +; %conv7 = zext i16 %2 to i32 +; %cmp8 = icmp ne i32 %conv7, 2032 +; br i1 %cmp8, label %if.then10, label %if.end11 +; +;if.then10: ; preds = %if.end6 +; call void @abort() #2 +; unreachable +; +;if.end11: ; preds = %if.end6 +; call void @srli() +; %3 = load i16* @us3, align 2 +; %conv12 = zext i16 %3 to i32 +; %cmp13 = icmp ne i32 %conv12, 4064 +; br i1 %cmp13, label %if.then15, label %if.end16 +; +;if.then15: ; preds = %if.end11 +; call void @abort() #2 +; unreachable +; +;if.end16: ; preds = %if.end11 +; call void @sra() +; %4 = load i16* @s3, align 2 +; %conv17 = sext i16 %4 to i32 +; %cmp18 = icmp ne i32 %conv17, -6 +; br i1 %cmp18, label %if.then20, label %if.end21 +; +;if.then20: ; preds = %if.end16 +; call void @abort() #2 +; unreachable +; +;if.end21: ; preds = %if.end16 +; call void @srai() +; %5 = load i16* @s3, align 2 +; %conv22 = sext i16 %5 to i32 +; %cmp23 = icmp ne i32 %conv22, -23 +; br i1 %cmp23, label %if.then25, label %if.end26 +; +;if.then25: ; preds = %if.end21 +; call void @abort() #2 +; unreachable +; +;if.end26: ; preds = %if.end21 +; ret i32 0 +;} + +; Function Attrs: noreturn +declare void @abort() #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { noreturn "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { noreturn } + +