Index: lib/Target/Mips/MipsFastISel.cpp =================================================================== --- lib/Target/Mips/MipsFastISel.cpp +++ lib/Target/Mips/MipsFastISel.cpp @@ -4,6 +4,7 @@ #include "llvm/CodeGen/FunctionLoweringInfo.h" #include "llvm/CodeGen/FastISel.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/Target/TargetInstrInfo.h" @@ -73,9 +74,19 @@ unsigned Alignment = 0); bool EmitStore(MVT VT, unsigned SrcReg, Address &Addr, unsigned Alignment = 0); + + bool EmitCmp(unsigned DestReg, CmpInst::Predicate P, const Value *Left, + const Value *Right); bool SelectLoad(const Instruction *I); + bool SelectBranch(const Instruction *I); bool SelectRet(const Instruction *I); bool SelectStore(const Instruction *I); + bool SelectIntExt(const Instruction *I); + bool SelectTrunc(const Instruction *I); + bool SelectFPExt(const Instruction *I); + bool SelectFPTrunc(const Instruction *I); + bool SelectFPToI(const Instruction *I, bool IsSigned); + bool SelectCmp(const Instruction *I); bool isTypeLegal(Type *Ty, MVT &VT); bool isLoadTypeLegal(Type *Ty, MVT &VT); @@ -85,6 +96,10 @@ unsigned MaterializeInt(const Constant *C, MVT VT); unsigned Materialize32BitInt(int64_t Imm, const TargetRegisterClass *RC); + bool EmitIntZExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, unsigned DestReg); + + bool EmitIntSExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, unsigned DestReg); + // for some reason, this default is not generated by tablegen // so we explicitly generate it here. // @@ -109,7 +124,7 @@ } MachineInstrBuilder EmitInstLoad(unsigned Opc, unsigned DstReg, - unsigned MemReg, int64_t MemOffset) { + unsigned MemReg, int64_t MemOffset) { return EmitInst(Opc, DstReg).addReg(MemReg).addImm(MemOffset); } @@ -240,6 +255,68 @@ return true; } +// Attempt to emit an integer extend of SrcReg into DestReg. Both +// signed and zero extensions are supported. Return false if we +// can't handle it. +bool MipsFastISel::EmitIntSExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, + unsigned DestReg) { + if ((DestVT != MVT::i32) && (DestVT != MVT::i16)) + return false; + switch (SrcVT.SimpleTy) { + default: + return false; + case MVT::i8: + EmitInst(Mips::SEB, DestReg).addReg(SrcReg); + break; + case MVT::i16: + EmitInst(Mips::SEH, DestReg).addReg(SrcReg); + break; + } + return true; +} + +bool MipsFastISel::EmitIntZExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, + unsigned DestReg) { + switch (SrcVT.SimpleTy) { + default: + return false; + case MVT::i1: + EmitInst(Mips::ANDi, DestReg).addReg(SrcReg).addImm(1); + break; + case MVT::i8: + EmitInst(Mips::ANDi, DestReg).addReg(SrcReg).addImm(0xff); + break; + case MVT::i16: + EmitInst(Mips::ANDi, DestReg).addReg(SrcReg).addImm(0xffff); + break; + } + return true; +} + +bool MipsFastISel::SelectBranch(const Instruction *I) { + const BranchInst *BI = cast(I); + MachineBasicBlock *BrBB = FuncInfo.MBB; + MachineBasicBlock *TBB = FuncInfo.MBBMap[BI->getSuccessor(0)]; + MachineBasicBlock *FBB = FuncInfo.MBBMap[BI->getSuccessor(1)]; + BI->getCondition(); + // For now, just try the simplest case where it's fed by a compare. + if (const CmpInst *CI = dyn_cast(BI->getCondition())) { + CmpInst::Predicate P = CI->getPredicate(); + unsigned CondReg = createResultReg(&Mips::GPR32RegClass); + + if (!EmitCmp(CondReg, P, CI->getOperand(0), CI->getOperand(1))) + return false; + + BuildMI(*BrBB, FuncInfo.InsertPt, DbgLoc, TII.get(Mips::BGTZ)) + .addReg(CondReg) + .addMBB(TBB); + FastEmitBranch(FBB, DbgLoc); + FuncInfo.MBB->addSuccessor(TBB); + return true; + } + return false; +} + bool MipsFastISel::SelectLoad(const Instruction *I) { // Atomic loads need special handling. if (cast(I)->isAtomic()) @@ -302,6 +379,304 @@ return true; } +// Attempt to fast-select a floating-point extend instruction. +bool MipsFastISel::SelectFPExt(const Instruction *I) { + Value *Src = I->getOperand(0); + EVT SrcVT = TLI.getValueType(Src->getType(), true); + EVT DestVT = TLI.getValueType(I->getType(), true); + + if (SrcVT != MVT::f32 || DestVT != MVT::f64) + return false; + + unsigned SrcReg = + getRegForValue(Src); // his must be a 32 bit floating point register class + // maybe we should handle this differently + if (!SrcReg) + return false; + + unsigned DestReg = createResultReg(&Mips::AFGR64RegClass); + EmitInst(Mips::CVT_D_S_MM, DestReg).addReg(SrcReg); + UpdateValueMap(I, DestReg); + return true; +} + +// Attempt to fast-select a floating-point truncate instruction. +bool MipsFastISel::SelectFPTrunc(const Instruction *I) { + Value *Src = I->getOperand(0); + EVT SrcVT = TLI.getValueType(Src->getType(), true); + EVT DestVT = TLI.getValueType(I->getType(), true); + + if (SrcVT != MVT::f64 || DestVT != MVT::f32) + return false; + + unsigned SrcReg = + getRegForValue(Src); // his must be a 32 bit floating point register class + // maybe we should handle this differently + if (!SrcReg) + return false; + + unsigned DestReg = createResultReg(&Mips::FGR32RegClass); + EmitInst(Mips::CVT_S_D32, DestReg).addReg(SrcReg); + UpdateValueMap(I, DestReg); + return true; +} + +bool MipsFastISel::SelectIntExt(const Instruction *I) { + // On ARM, in general, integer casts don't involve legal types; this code + // handles promotable integers. + Type *DestTy = I->getType(); + Value *Src = I->getOperand(0); + Type *SrcTy = Src->getType(); + + bool isZExt = isa(I); + unsigned SrcReg = getRegForValue(Src); + if (!SrcReg) + return false; + + EVT SrcEVT, DestEVT; + SrcEVT = TLI.getValueType(SrcTy, true); + DestEVT = TLI.getValueType(DestTy, true); + if (!SrcEVT.isSimple()) + return false; + if (!DestEVT.isSimple()) + return false; + + MVT SrcVT = SrcEVT.getSimpleVT(); + MVT DestVT = DestEVT.getSimpleVT(); + unsigned ResultReg = createResultReg(&Mips::GPR32RegClass); + + if (isZExt) { + if (!EmitIntZExt(SrcVT, SrcReg, DestVT, ResultReg)) + return false; + } else { + if (!EmitIntSExt(SrcVT, SrcReg, DestVT, ResultReg)) + return false; + } + UpdateValueMap(I, ResultReg); + return true; +} + +bool MipsFastISel::SelectTrunc(const Instruction *I) { + // The high bits for a type smaller than the register size are assumed to be + // undefined. + Value *Op = I->getOperand(0); + + EVT SrcVT, DestVT; + SrcVT = TLI.getValueType(Op->getType(), true); + DestVT = TLI.getValueType(I->getType(), true); + + if (SrcVT != MVT::i32 && SrcVT != MVT::i16 && SrcVT != MVT::i8) + return false; + if (DestVT != MVT::i16 && DestVT != MVT::i8 && DestVT != MVT::i1) + return false; + + unsigned SrcReg = getRegForValue(Op); + if (!SrcReg) + return false; + + // Because the high bits are undefined, a truncate doesn't generate + // any code. + UpdateValueMap(I, SrcReg); + return true; +} + +// Attempt to fast-select a floating-point-to-integer conversion. +bool MipsFastISel::SelectFPToI(const Instruction *I, bool IsSigned) { + MVT DstVT, SrcVT; + if (!IsSigned) + return false; // we don't handle this case yet. there is no native + // instruction for this but it can be synthesized. + Type *DstTy = I->getType(); + if (!isTypeLegal(DstTy, DstVT)) + return false; + + if (DstVT != MVT::i32) + return false; + + Value *Src = I->getOperand(0); + Type *SrcTy = Src->getType(); + if (!isTypeLegal(SrcTy, SrcVT)) + return false; + + if (SrcVT != MVT::f32 && SrcVT != MVT::f64) + return false; + + unsigned SrcReg = getRegForValue(Src); + if (SrcReg == 0) + return false; + + // Determine the opcode for the conversion, which takes place + // entirely within FPRs. + unsigned DestReg = createResultReg(&Mips::GPR32RegClass); + unsigned TempReg = createResultReg(&Mips::FGR32RegClass); + unsigned Opc; + + if (SrcVT == MVT::f32) + Opc = Mips::TRUNC_W_S; + else + Opc = Mips::TRUNC_W_D32; + + // Generate the convert. + EmitInst(Opc, TempReg).addReg(SrcReg); + + EmitInst(Mips::MFC1, DestReg).addReg(TempReg); + + UpdateValueMap(I, DestReg); + return true; +} + +bool MipsFastISel::EmitCmp(unsigned ResultReg, CmpInst::Predicate P, + const Value *Left, const Value *Right) { + unsigned LeftReg = getRegForValue(Left); + unsigned RightReg = getRegForValue(Right); + switch (P) { + default: + return false; + case CmpInst::ICMP_EQ: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::XOR, TempReg).addReg(LeftReg).addReg(RightReg); + EmitInst(Mips::SLTu, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::ICMP_NE: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::XOR, TempReg).addReg(LeftReg).addReg(RightReg); + EmitInst(Mips::SLTu, ResultReg).addReg(Mips::ZERO).addReg(TempReg); + break; + } + case CmpInst::ICMP_UGT: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::SLTu, TempReg).addReg(RightReg).addReg(LeftReg); + EmitInst(Mips::ANDi, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::ICMP_ULT: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::SLTu, TempReg).addReg(LeftReg).addReg(RightReg); + EmitInst(Mips::ANDi, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::ICMP_UGE: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + unsigned TempReg2 = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::SLTu, TempReg).addReg(LeftReg).addReg(RightReg); + EmitInst(Mips::XORi, TempReg2).addReg(TempReg).addImm(1); + EmitInst(Mips::ANDi, ResultReg).addReg(TempReg2).addImm(1); + break; + } + case CmpInst::ICMP_ULE: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + unsigned TempReg2 = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::SLTu, TempReg).addReg(RightReg).addReg(LeftReg); + EmitInst(Mips::XORi, TempReg2).addReg(TempReg).addImm(1); + EmitInst(Mips::ANDi, ResultReg).addReg(TempReg2).addImm(1); + break; + } + case CmpInst::ICMP_SGT: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::SLT, TempReg).addReg(RightReg).addReg(LeftReg); + EmitInst(Mips::ANDi, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::ICMP_SLT: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::SLT, TempReg).addReg(LeftReg).addReg(RightReg); + EmitInst(Mips::ANDi, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::ICMP_SGE: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + unsigned TempReg2 = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::SLT, TempReg).addReg(LeftReg).addReg(RightReg); + EmitInst(Mips::XORi, TempReg2).addReg(TempReg).addImm(1); + EmitInst(Mips::ANDi, ResultReg).addReg(TempReg2).addImm(1); + break; + } + case CmpInst::ICMP_SLE: { + unsigned TempReg = createResultReg(&Mips::GPR32RegClass); + unsigned TempReg2 = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::SLT, TempReg).addReg(RightReg).addReg(LeftReg); + EmitInst(Mips::XORi, TempReg2).addReg(TempReg).addImm(1); + EmitInst(Mips::ANDi, ResultReg).addReg(TempReg2).addImm(1); + break; + } + case CmpInst::FCMP_OEQ: + case CmpInst::FCMP_UNE: + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_OLE: + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_OGE: { + bool IsFloat = Left->getType()->isFloatTy(); + bool IsDouble = Left->getType()->isDoubleTy(); + if (!IsFloat && !IsDouble) + return false; + unsigned Opc, CondMovOpc; + switch (P) { + case CmpInst::FCMP_OEQ: + Opc = IsFloat ? Mips::C_EQ_S : Mips::C_EQ_D32; + CondMovOpc = Mips::MOVT_I; + break; + case CmpInst::FCMP_UNE: + Opc = IsFloat ? Mips::C_EQ_S : Mips::C_EQ_D32; + CondMovOpc = Mips::MOVF_I; + break; + case CmpInst::FCMP_OLT: + Opc = IsFloat ? Mips::C_OLT_S : Mips::C_OLT_D32; + CondMovOpc = Mips::MOVT_I; + break; + case CmpInst::FCMP_OLE: + Opc = IsFloat ? Mips::C_OLE_S : Mips::C_OLE_D32; + CondMovOpc = Mips::MOVT_I; + break; + case CmpInst::FCMP_OGT: + Opc = IsFloat ? Mips::C_OLE_S : Mips::C_OLE_D32; + CondMovOpc = Mips::MOVF_I; + break; + case CmpInst::FCMP_OGE: + Opc = IsFloat ? Mips::C_OLT_S : Mips::C_OLT_D32; + CondMovOpc = Mips::MOVF_I; + break; + default: + break; + } + unsigned RegWithZero = createResultReg(&Mips::GPR32RegClass); + unsigned RegWithOne = createResultReg(&Mips::GPR32RegClass); + EmitInst(Mips::ADDiu, RegWithZero).addReg(Mips::ZERO).addImm(0); + EmitInst(Mips::ADDiu, RegWithOne).addReg(Mips::ZERO).addImm(1); + EmitInst(Opc).addReg(LeftReg).addReg(RightReg).addReg( + Mips::FCC0, RegState::ImplicitDefine); + MachineInstrBuilder MI = EmitInst(CondMovOpc, ResultReg) + .addReg(RegWithOne) + .addReg(Mips::FCC0) + .addReg(RegWithZero, RegState::Implicit); + MI->tieOperands(0, 3); + break; + } + case CmpInst::FCMP_ONE: + case CmpInst::FCMP_UEQ: + case CmpInst::FCMP_UGT: + case CmpInst::FCMP_ULT: + case CmpInst::FCMP_UGE: + case CmpInst::FCMP_ULE: + case CmpInst::FCMP_ORD: + case CmpInst::FCMP_UNO: + case CmpInst::FCMP_TRUE: + case CmpInst::FCMP_FALSE: + return false; + } + return true; +} +bool MipsFastISel::SelectCmp(const Instruction *I) { + const CmpInst *CI = cast(I); + const Value *Left = I->getOperand(0), *Right = I->getOperand(1); + unsigned ResultReg = createResultReg(&Mips::GPR32RegClass); + CmpInst::Predicate P = CI->getPredicate(); + if (!EmitCmp(ResultReg, P, Left, Right)) + return false; + UpdateValueMap(I, ResultReg); + return true; +} + bool MipsFastISel::TargetSelectInstruction(const Instruction *I) { if (!TargetSupported) return false; @@ -312,8 +687,26 @@ return SelectLoad(I); case Instruction::Store: return SelectStore(I); + case Instruction::Br: + return SelectBranch(I); case Instruction::Ret: return SelectRet(I); + case Instruction::Trunc: + return SelectTrunc(I); + case Instruction::ZExt: + case Instruction::SExt: + return SelectIntExt(I); + case Instruction::FPTrunc: + return SelectFPTrunc(I); + case Instruction::FPExt: + return SelectFPExt(I); + case Instruction::FPToSI: + return SelectFPToI(I, /*isSigned*/ true); + case Instruction::FPToUI: + return SelectFPToI(I, /*isSigned*/ false); + case Instruction::ICmp: + case Instruction::FCmp: + return SelectCmp(I); } return false; } Index: test/CodeGen/Mips/Fast-ISel/br1.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/Fast-ISel/br1.ll @@ -0,0 +1,32 @@ +; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32r2 \ +; RUN: < %s | FileCheck %s + +@b = global i32 1, align 4 +@i = global i32 0, align 4 +@.str = private unnamed_addr constant [5 x i8] c"%i \0A\00", align 1 + +; Function Attrs: nounwind +define void @br() #0 { +entry: + %0 = load i32* @b, align 4 + %tobool = icmp eq i32 %0, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + store i32 6754, i32* @i, align 4 + br label %if.end + +if.end: ; preds = %entry, %if.then + ret void +; CHECK: xor $[[REG1:[0-9]+]], ${{[0-9]+}}, $zero +; CHECK: sltu $[[REG2:[0-9]+]], $[[REG1]], 1 +; CHECK: bgtz $[[REG2]], $BB[[BL:[0-9]+_[0-9]+]] +; CHECK: nop +; CHECK: addiu ${{[0-9]+}}, $zero, 6754 +; CHECK: sw ${{[0-9]+}}, 0(${{[0-9]+}}) +; CHECK: $BB[[BL]]: + +} + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + Index: test/CodeGen/Mips/Fast-ISel/fpcmpa.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/Fast-ISel/fpcmpa.ll @@ -0,0 +1,182 @@ +; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32r2 \ +; RUN: < %s | FileCheck %s + +@f1 = common global float 0.000000e+00, align 4 +@f2 = common global float 0.000000e+00, align 4 +@b1 = common global i32 0, align 4 +@d1 = common global double 0.000000e+00, align 8 +@d2 = common global double 0.000000e+00, align 8 + +; Function Attrs: nounwind +define void @feq1() #0 { +entry: + %0 = load float* @f1, align 4 + %1 = load float* @f2, align 4 + %cmp = fcmp oeq float %0, %1 +; CHECK-LABEL: feq1: +; CHECK: c.eq.s $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movt ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @fne1() #0 { +entry: + %0 = load float* @f1, align 4 + %1 = load float* @f2, align 4 + %cmp = fcmp une float %0, %1 +; CHECK-LABEL: fne1: +; CHECK: c.eq.s $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movf ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @flt1() #0 { +entry: + %0 = load float* @f1, align 4 + %1 = load float* @f2, align 4 + %cmp = fcmp olt float %0, %1 +; CHECK-LABEL: flt1: +; CHECK: c.olt.s $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movt ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @fgt1() #0 { +entry: + %0 = load float* @f1, align 4 + %1 = load float* @f2, align 4 + %cmp = fcmp ogt float %0, %1 +; CHECK-LABEL: fgt1: +; CHECK: c.ole.s $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movf ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @fle1() #0 { +entry: + %0 = load float* @f1, align 4 + %1 = load float* @f2, align 4 + %cmp = fcmp ole float %0, %1 +; CHECK-LABEL: fle1: +; CHECK: c.ole.s $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movt ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @fge1() #0 { +entry: + %0 = load float* @f1, align 4 + %1 = load float* @f2, align 4 + %cmp = fcmp oge float %0, %1 +; CHECK-LABEL: fge1: +; CHECK: c.olt.s $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movf ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @deq1() #0 { +entry: + %0 = load double* @d1, align 8 + %1 = load double* @d2, align 8 + %cmp = fcmp oeq double %0, %1 +; CHECK-LABEL: deq1: +; CHECK: c.eq.d $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movt ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @dne1() #0 { +entry: + %0 = load double* @d1, align 8 + %1 = load double* @d2, align 8 + %cmp = fcmp une double %0, %1 +; CHECK-LABEL: dne1: +; CHECK: c.eq.d $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movf ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @dlt1() #0 { +entry: + %0 = load double* @d1, align 8 + %1 = load double* @d2, align 8 + %cmp = fcmp olt double %0, %1 +; CHECK-LABEL: dlt1: +; CHECK: c.olt.d $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movt ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @dgt1() #0 { +entry: + %0 = load double* @d1, align 8 + %1 = load double* @d2, align 8 + %cmp = fcmp ogt double %0, %1 +; CHECK-LABEL: dgt1: +; CHECK: c.ole.d $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movf ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @dle1() #0 { +entry: + %0 = load double* @d1, align 8 + %1 = load double* @d2, align 8 + %cmp = fcmp ole double %0, %1 +; CHECK-LABEL: dle1: +; CHECK: c.ole.d $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movt ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @dge1() #0 { +entry: + %0 = load double* @d1, align 8 + %1 = load double* @d2, align 8 + %cmp = fcmp oge double %0, %1 +; CHECK-LABEL: dge1: +; CHECK: c.olt.d $f{{[0-9]+}}, $f{{[0-9]+}} +; CHECK: movf ${{[0-9]+}}, ${{[0-9]+}}, $fcc0 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 + ret void +} + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + + Index: test/CodeGen/Mips/Fast-ISel/fpext.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/Fast-ISel/fpext.ll @@ -0,0 +1,21 @@ +; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32r2 \ +; RUN: < %s | FileCheck %s + +@f = global float 0x40147E6B80000000, align 4 +@d_f = common global double 0.000000e+00, align 8 +@.str = private unnamed_addr constant [6 x i8] c"%f \0A\00", align 1 + +; Function Attrs: nounwind +define void @dv() #0 { +entry: + %0 = load float* @f, align 4 + %conv = fpext float %0 to double +; CHECK: cvt.d.s $f{{[0-9]+}}, $f{{[0-9]+}} + store double %conv, double* @d_f, align 8 + ret void +} + + +attributes #1 = { nounwind } + + Index: test/CodeGen/Mips/Fast-ISel/fpintconv.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/Fast-ISel/fpintconv.ll @@ -0,0 +1,63 @@ +; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32r2 \ +; RUN: < %s | FileCheck %s + + +@f = global float 0x40D6E83280000000, align 4 +@d = global double 0x4132D68780000000, align 8 +@i_f = common global i32 0, align 4 +@i_d = common global i32 0, align 4 +@.str = private unnamed_addr constant [5 x i8] c"%i \0A\00", align 1 + +; Function Attrs: nounwind +define void @ifv() #0 { +entry: +; CHECK: .ent ifv + %0 = load float* @f, align 4, !tbaa !1 + %conv = fptosi float %0 to i32 +; CHECK: trunc.w.s $f{{[0-9]+}}, $f{{[0-9]+}} + store i32 %conv, i32* @i_f, align 4, !tbaa !5 + ret void +} + +; Function Attrs: nounwind +define void @idv() #0 { +entry: +; CHECK: .ent idv + %0 = load double* @d, align 8, !tbaa !7 + %conv = fptosi double %0 to i32 +; CHECK: trunc.w.d $f{{[0-9]+}}, $f{{[0-9]+}} + store i32 %conv, i32* @i_d, align 4, !tbaa !5 + ret void +} + +; Function Attrs: nounwind +define i32 @main() #0 { +entry: + %0 = load float* @f, align 4, !tbaa !1 + %conv.i = fptosi float %0 to i32 + store i32 %conv.i, i32* @i_f, align 4, !tbaa !5 + %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([5 x i8]* @.str, i32 0, i32 0), i32 %conv.i) #1 + %1 = load double* @d, align 8, !tbaa !7 + %conv.i2 = fptosi double %1 to i32 + store i32 %conv.i2, i32* @i_d, align 4, !tbaa !5 + %call1 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([5 x i8]* @.str, i32 0, i32 0), i32 %conv.i2) #1 + ret i32 0 +} + +; Function Attrs: nounwind +declare i32 @printf(i8* nocapture readonly, ...) #0 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "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 = { nounwind } + +!llvm.ident = !{!0} + +!0 = metadata !{metadata !"clang version 3.5.0 (gitosis@dmz-portal.mips.com:clang.git f4545688eab4ddb6fb4dabe43d9d00f9d7db9602) (gitosis@dmz-portal.mips.com:llvm.git fe236d3fc6882ea8f4bfebdfee82766b0356506c)"} +!1 = metadata !{metadata !2, metadata !2, i64 0} +!2 = metadata !{metadata !"float", metadata !3, i64 0} +!3 = metadata !{metadata !"omnipotent char", metadata !4, i64 0} +!4 = metadata !{metadata !"Simple C/C++ TBAA"} +!5 = metadata !{metadata !6, metadata !6, i64 0} +!6 = metadata !{metadata !"int", metadata !3, i64 0} +!7 = metadata !{metadata !8, metadata !8, i64 0} +!8 = metadata !{metadata !"double", metadata !3, i64 0} Index: test/CodeGen/Mips/Fast-ISel/fptrunc.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/Fast-ISel/fptrunc.ll @@ -0,0 +1,20 @@ +; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32r2 \ +; RUN: < %s | FileCheck %s + +@d = global double 0x40147E6B74DF0446, align 8 +@f = common global float 0.000000e+00, align 4 +@.str = private unnamed_addr constant [6 x i8] c"%f \0A\00", align 1 + +; Function Attrs: nounwind +define void @fv() #0 { +entry: + %0 = load double* @d, align 8 + %conv = fptrunc double %0 to float +; CHECK: cvt.s.d $f{{[0-9]+}}, $f{{[0-9]+}} + store float %conv, float* @f, align 4 + ret void +} + +attributes #1 = { nounwind } + + Index: test/CodeGen/Mips/Fast-ISel/icmpa.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/Fast-ISel/icmpa.ll @@ -0,0 +1,162 @@ +; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32r2 \ +; RUN: < %s | FileCheck %s + +@c = global i32 4, align 4 +@d = global i32 9, align 4 +@uc = global i32 4, align 4 +@ud = global i32 9, align 4 +@b1 = common global i32 0, align 4 + +; Function Attrs: nounwind +define void @eq() #0 { +entry: +; CHECK: .ent eq + + %0 = load i32* @c, align 4 + %1 = load i32* @d, align 4 + %cmp = icmp eq i32 %0, %1 + %conv = zext i1 %cmp to i32 +; CHECK: xor ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: sltu ${{[0-9]+}}, ${{[0-9]+}}, 1 +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @ne() #0 { +entry: +; CHECK: .ent ne + %0 = load i32* @c, align 4 + %1 = load i32* @d, align 4 + %cmp = icmp ne i32 %0, %1 + %conv = zext i1 %cmp to i32 +; CHECK: xor ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: sltu ${{[0-9]+}}, $zero, ${{[0-9]+}} +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @ugt() #0 { +entry: +; CHECK: .ent ugt + %0 = load i32* @uc, align 4 + %1 = load i32* @ud, align 4 + %cmp = icmp ugt i32 %0, %1 + %conv = zext i1 %cmp to i32 +; CHECK: sltu ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @ult() #0 { +entry: +; CHECK: .ent ult + %0 = load i32* @uc, align 4 + %1 = load i32* @ud, align 4 + %cmp = icmp ult i32 %0, %1 + %conv = zext i1 %cmp to i32 +; CHECK: sltu ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @uge() #0 { +entry: +; CHECK: .ent uge + %0 = load i32* @uc, align 4 + %1 = load i32* @ud, align 4 + %cmp = icmp uge i32 %0, %1 + %conv = zext i1 %cmp to i32 +; CHECK: sltu ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: xori ${{[0-9]+}}, ${{[0-9]+}}, 1 +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @ule() #0 { +entry: +; CHECK: .ent ule + %0 = load i32* @uc, align 4 + %1 = load i32* @ud, align 4 + %cmp = icmp ule i32 %0, %1 + %conv = zext i1 %cmp to i32 +; CHECK: sltu ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: xori ${{[0-9]+}}, ${{[0-9]+}}, 1 +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @sgt() #0 { +entry: +; CHECK: .ent sgt + %0 = load i32* @c, align 4 + %1 = load i32* @d, align 4 + %cmp = icmp sgt i32 %0, %1 + %conv = zext i1 %cmp to i32 +; CHECK: slt ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @slt() #0 { +entry: +; CHECK: .ent slt + %0 = load i32* @c, align 4 + %1 = load i32* @d, align 4 + %cmp = icmp slt i32 %0, %1 + %conv = zext i1 %cmp to i32 +; CHECK: slt ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + store i32 %conv, i32* @b1, align 4 + ret void +} + +; Function Attrs: nounwind +define void @sge() #0 { +entry: +; CHECK: .ent sge + %0 = load i32* @c, align 4 + %1 = load i32* @d, align 4 + %cmp = icmp sge i32 %0, %1 + %conv = zext i1 %cmp to i32 + store i32 %conv, i32* @b1, align 4 +; CHECK: slt ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: xori ${{[0-9]+}}, ${{[0-9]+}}, 1 +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + ret void +} + +; Function Attrs: nounwind +define void @sle() #0 { +entry: +; CHECK: .ent sle + %0 = load i32* @c, align 4 + %1 = load i32* @d, align 4 + %cmp = icmp sle i32 %0, %1 + %conv = zext i1 %cmp to i32 +; CHECK: slt ${{[0-9]+}}, ${{[0-9]+}}, ${{[0-9]+}} +; CHECK: xori ${{[0-9]+}}, ${{[0-9]+}}, 1 +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 + store i32 %conv, i32* @b1, align 4 + ret void +} + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + + Index: test/CodeGen/Mips/Fast-ISel/loadstore2.ll =================================================================== --- test/CodeGen/Mips/Fast-ISel/loadstore2.ll +++ /dev/null @@ -1,83 +0,0 @@ -; ModuleID = 'loadstore2.c' -target datalayout = "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64" -target triple = "mips--linux-gnu" - -@c2 = common global i8 0, align 1 -@c1 = common global i8 0, align 1 -; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32r2 \ -; RUN: < %s | FileCheck %s - -@s2 = common global i16 0, align 2 -@s1 = common global i16 0, align 2 -@i2 = common global i32 0, align 4 -@i1 = common global i32 0, align 4 -@f2 = common global float 0.000000e+00, align 4 -@f1 = common global float 0.000000e+00, align 4 -@d2 = common global double 0.000000e+00, align 8 -@d1 = common global double 0.000000e+00, align 8 - -; Function Attrs: nounwind -define void @cfoo() #0 { -entry: - %0 = load i8* @c2, align 1 - store i8 %0, i8* @c1, align 1 -; CHECK-LABEL: cfoo: -; CHECK: lbu $[[REGc:[0-9]+]], 0(${{[0-9]+}}) -; CHECK: sb $[[REGc]], 0(${{[0-9]+}}) - - - ret void -} - -; Function Attrs: nounwind -define void @sfoo() #0 { -entry: - %0 = load i16* @s2, align 2 - store i16 %0, i16* @s1, align 2 -; CHECK-LABEL: sfoo: -; CHECK: lhu $[[REGs:[0-9]+]], 0(${{[0-9]+}}) -; CHECK: sh $[[REGs]], 0(${{[0-9]+}}) - - ret void -} - -; Function Attrs: nounwind -define void @ifoo() #0 { -entry: - %0 = load i32* @i2, align 4 - store i32 %0, i32* @i1, align 4 -; CHECK-LABEL: ifoo: -; CHECK: lw $[[REGi:[0-9]+]], 0(${{[0-9]+}}) -; CHECK: sw $[[REGi]], 0(${{[0-9]+}}) - - ret void -} - -; Function Attrs: nounwind -define void @ffoo() #0 { -entry: - %0 = load float* @f2, align 4 - store float %0, float* @f1, align 4 -; CHECK-LABEL: ffoo: -; CHECK: lwc1 $f[[REGf:[0-9]+]], 0(${{[0-9]+}}) -; CHECK: swc1 $f[[REGf]], 0(${{[0-9]+}}) - - - ret void -} - -; Function Attrs: nounwind -define void @dfoo() #0 { -entry: - %0 = load double* @d2, align 8 - store double %0, double* @d1, align 8 -; CHECK-LABEL: dfoo: -; CHECK: ldc1 $f[[REGd:[0-9]+]], 0(${{[0-9]+}}) -; CHECK: sdc1 $f[[REGd]], 0(${{[0-9]+}}) -; CHECK: .end dfoo - ret void -} - -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" } - - Index: test/CodeGen/Mips/Fast-ISel/loadstoreconv.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/Fast-ISel/loadstoreconv.ll @@ -0,0 +1,166 @@ +; RUN: llc -march=mipsel -relocation-model=pic -O0 -mips-fast-isel -fast-isel-abort -mcpu=mips32r2 \ +; RUN: < %s | FileCheck %s + +@b2 = global i8 0, align 1 +@b1 = global i8 1, align 1 +@uc1 = global i8 0, align 1 +@uc2 = global i8 -1, align 1 +@sc1 = global i8 -128, align 1 +@sc2 = global i8 127, align 1 +@ss1 = global i16 -32768, align 2 +@ss2 = global i16 32767, align 2 +@us1 = global i16 0, align 2 +@us2 = global i16 -1, align 2 +@ssi = global i16 0, align 2 +@ssj = global i16 0, align 2 +@i = global i32 0, align 4 +@j = global i32 0, align 4 +@.str = private unnamed_addr constant [4 x i8] c"%i\0A\00", align 1 +@.str1 = private unnamed_addr constant [7 x i8] c"%i %i\0A\00", align 1 + +; Function Attrs: nounwind +define void @_Z3b_iv() #0 { +entry: +; CHECK: .ent _Z3b_iv + %0 = load i8* @b1, align 1 + %tobool = trunc i8 %0 to i1 + %frombool = zext i1 %tobool to i8 + store i8 %frombool, i8* @b2, align 1 + %1 = load i8* @b2, align 1 + %tobool1 = trunc i8 %1 to i1 + %conv = zext i1 %tobool1 to i32 + store i32 %conv, i32* @i, align 4 +; CHECK: lbu ${{[0-9]+}}, 0(${{[0-9]+}}) +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 +; CHECK: sb ${{[0-9]+}}, 0(${{[0-9]+}}) + + + + ret void +; CHECK: .end _Z3b_iv +} + +; Function Attrs: nounwind +define void @_Z4uc_iv() #0 { +entry: +; CHECK: .ent _Z4uc_iv + + %0 = load i8* @uc1, align 1 + %conv = zext i8 %0 to i32 + store i32 %conv, i32* @i, align 4 + %1 = load i8* @uc2, align 1 + %conv1 = zext i8 %1 to i32 +; CHECK: lbu ${{[0-9]+}}, 0(${{[0-9]+}}) +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 255 + + store i32 %conv1, i32* @j, align 4 + ret void +; CHECK: .end _Z4uc_iv + +} + +; Function Attrs: nounwind +define void @_Z4sc_iv() #0 { +entry: +; CHECK: .ent _Z4sc_iv + + %0 = load i8* @sc1, align 1 + %conv = sext i8 %0 to i32 + store i32 %conv, i32* @i, align 4 + %1 = load i8* @sc2, align 1 + %conv1 = sext i8 %1 to i32 + store i32 %conv1, i32* @j, align 4 +; CHECK: lbu ${{[0-9]+}}, 0(${{[0-9]+}}) +; CHECK: seb ${{[0-9]+}}, ${{[0-9]+}} + + ret void +; CHECK: .end _Z4sc_iv +} + +; Function Attrs: nounwind +define void @_Z4us_iv() #0 { +entry: +; CHECK: .ent _Z4us_iv + %0 = load i16* @us1, align 2 + %conv = zext i16 %0 to i32 + store i32 %conv, i32* @i, align 4 + %1 = load i16* @us2, align 2 + %conv1 = zext i16 %1 to i32 + store i32 %conv1, i32* @j, align 4 + ret void +; CHECK: lhu ${{[0-9]+}}, 0(${{[0-9]+}}) +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 65535 +; CHECK: .end _Z4us_iv +} + +; Function Attrs: nounwind +define void @_Z4ss_iv() #0 { +entry: +; CHECK: .ent _Z4ss_iv + + %0 = load i16* @ss1, align 2 + %conv = sext i16 %0 to i32 + store i32 %conv, i32* @i, align 4 + %1 = load i16* @ss2, align 2 + %conv1 = sext i16 %1 to i32 + store i32 %conv1, i32* @j, align 4 +; CHECK: lhu ${{[0-9]+}}, 0(${{[0-9]+}}) +; CHECK: seh ${{[0-9]+}}, ${{[0-9]+}} + + ret void +; CHECK: .end _Z4ss_iv +} + +; Function Attrs: nounwind +define void @_Z4b_ssv() #0 { +entry: +; CHECK: .ent _Z4b_ssv + %0 = load i8* @b2, align 1 + %tobool = trunc i8 %0 to i1 + %conv = zext i1 %tobool to i16 + store i16 %conv, i16* @ssi, align 2 + ret void +; CHECK: lbu ${{[0-9]+}}, 0(${{[0-9]+}}) +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 1 +; CHECK: .end _Z4b_ssv +} + +; Function Attrs: nounwind +define void @_Z5uc_ssv() #0 { +entry: +; CHECK: .ent _Z5uc_ssv + %0 = load i8* @uc1, align 1 + %conv = zext i8 %0 to i16 + store i16 %conv, i16* @ssi, align 2 + %1 = load i8* @uc2, align 1 + %conv1 = zext i8 %1 to i16 +; CHECK: lbu ${{[0-9]+}}, 0(${{[0-9]+}}) +; CHECK: andi ${{[0-9]+}}, ${{[0-9]+}}, 255 + + store i16 %conv1, i16* @ssj, align 2 + ret void +; CHECK: .end _Z5uc_ssv +} + +; Function Attrs: nounwind +define void @_Z5sc_ssv() #0 { +entry: +; CHECK: .ent _Z5sc_ssv + %0 = load i8* @sc1, align 1 + %conv = sext i8 %0 to i16 + store i16 %conv, i16* @ssi, align 2 + %1 = load i8* @sc2, align 1 + %conv1 = sext i8 %1 to i16 + store i16 %conv1, i16* @ssj, align 2 +; CHECK: lbu ${{[0-9]+}}, 0(${{[0-9]+}}) +; CHECK: seb ${{[0-9]+}}, ${{[0-9]+}} + + ret void +; CHECK: .end _Z5sc_ssv +} + +declare i32 @printf(i8*, ...) #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 = { "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" } +