Index: llvm/include/llvm/CodeGen/ValueTypes.td =================================================================== --- llvm/include/llvm/CodeGen/ValueTypes.td +++ llvm/include/llvm/CodeGen/ValueTypes.td @@ -198,3 +198,14 @@ ValueType { int AddrSpace = addrspace; } + +/// This class represents nullptr value. First two arguments are same as for +/// PtrValueType, third is actual integer value that represents nullptr in +/// addrspace. Targets must define their own null pointers derived from this +/// class. GlobalISel Emmiter matches NullPtrValue leaf with: +/// %0:gprb(scalar) = G_CONSTANT intvalue +/// %1:gprb(p_addrspace) = G_INTTOPTR %0(scalar) +class NullPtrValue : + PtrValueType { + int IntValue = intvalue; +} Index: llvm/lib/Target/Mips/CMakeLists.txt =================================================================== --- llvm/lib/Target/Mips/CMakeLists.txt +++ llvm/lib/Target/Mips/CMakeLists.txt @@ -1,3 +1,8 @@ +set(LLVM_TARGET_DEFINITIONS MipsGISel.td) + +tablegen(LLVM MipsGenGlobalISel.inc -gen-global-isel) +tablegen(LLVM MipsGenRegisterBank.inc -gen-register-bank) + set(LLVM_TARGET_DEFINITIONS Mips.td) tablegen(LLVM MipsGenAsmMatcher.inc -gen-asm-matcher) @@ -6,11 +11,9 @@ tablegen(LLVM MipsGenDAGISel.inc -gen-dag-isel) tablegen(LLVM MipsGenDisassemblerTables.inc -gen-disassembler) tablegen(LLVM MipsGenFastISel.inc -gen-fast-isel) -tablegen(LLVM MipsGenGlobalISel.inc -gen-global-isel) tablegen(LLVM MipsGenInstrInfo.inc -gen-instr-info) tablegen(LLVM MipsGenMCCodeEmitter.inc -gen-emitter) tablegen(LLVM MipsGenMCPseudoLowering.inc -gen-pseudo-lowering) -tablegen(LLVM MipsGenRegisterBank.inc -gen-register-bank) tablegen(LLVM MipsGenRegisterInfo.inc -gen-register-info) tablegen(LLVM MipsGenSubtargetInfo.inc -gen-subtarget) tablegen(LLVM MipsGenExegesis.inc -gen-exegesis) Index: llvm/lib/Target/Mips/MipsGISel.td =================================================================== --- /dev/null +++ llvm/lib/Target/Mips/MipsGISel.td @@ -0,0 +1,33 @@ +include "Mips.td" + +def p0 : PtrValueType; + + // Pointer compare + def : MipsPat<(seteq p0:$lhs, p0:$rhs), + (SLTiu (XOR GPR32:$lhs, GPR32:$rhs), 1)>, ISA_MIPS1; + def : MipsPat<(setne p0:$lhs, p0:$rhs), + (SLTu ZERO, (XOR GPR32:$lhs, GPR32:$rhs))>, ISA_MIPS1; + def : MipsPat<(setult p0:$lhs, p0:$rhs), + (SLTu GPR32:$rhs, GPR32:$lhs)>, ISA_MIPS1; + def : MipsPat<(setule p0:$lhs, p0:$rhs), + (XORi (SLTu GPR32:$rhs, GPR32:$lhs), 1)>, ISA_MIPS1; + def : MipsPat<(setugt p0:$lhs, p0:$rhs), + (SLTu GPR32:$rhs, GPR32:$lhs)>, ISA_MIPS1; + def : MipsPat<(setuge p0:$lhs, p0:$rhs), + (XORi (SLTu GPR32:$lhs, GPR32:$rhs), 1)>, ISA_MIPS1; + + // Pointer select + def : MipsPat<(select GPR32:$cond, p0:$T, p0:$F), + (MOVN_I_I GPR32:$T, GPR32:$cond, GPR32:$F)>, + INSN_MIPS4_32_NOT_32R6_64R6; + +def p0Nullptr : NullPtrValue; + + // nullptr compare + def : MipsPat<(seteq p0:$lhs, p0Nullptr), + (SLTiu GPR32:$lhs, 1)>, ISA_MIPS1; + + // 'Select with nullptr compare' pattern + def : MipsPat<(select (i32 (setne p0:$lhs, p0Nullptr)),p0:$T, p0:$F), + (MOVN_I_I GPR32:$T, GPR32:$lhs, GPR32:$F)>, + INSN_MIPS4_32_NOT_32R6_64R6; Index: llvm/lib/Target/Mips/MipsInstructionSelector.cpp =================================================================== --- llvm/lib/Target/Mips/MipsInstructionSelector.cpp +++ llvm/lib/Target/Mips/MipsInstructionSelector.cpp @@ -463,15 +463,6 @@ I.eraseFromParent(); return true; } - case G_SELECT: { - // Handle operands with pointer type. - MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::MOVN_I_I)) - .add(I.getOperand(0)) - .add(I.getOperand(2)) - .add(I.getOperand(1)) - .add(I.getOperand(3)); - break; - } case G_IMPLICIT_DEF: { Register Dst = I.getOperand(0).getReg(); MI = BuildMI(MBB, I, I.getDebugLoc(), TII.get(Mips::IMPLICIT_DEF)) @@ -639,86 +630,6 @@ } break; } - case G_ICMP: { - struct Instr { - unsigned Opcode; - Register Def, LHS, RHS; - Instr(unsigned Opcode, Register Def, Register LHS, Register RHS) - : Opcode(Opcode), Def(Def), LHS(LHS), RHS(RHS){}; - - bool hasImm() const { - if (Opcode == Mips::SLTiu || Opcode == Mips::XORi) - return true; - return false; - } - }; - - SmallVector Instructions; - Register ICMPReg = I.getOperand(0).getReg(); - Register Temp = MRI.createVirtualRegister(&Mips::GPR32RegClass); - Register LHS = I.getOperand(2).getReg(); - Register RHS = I.getOperand(3).getReg(); - CmpInst::Predicate Cond = - static_cast(I.getOperand(1).getPredicate()); - - switch (Cond) { - case CmpInst::ICMP_EQ: // LHS == RHS -> (LHS ^ RHS) < 1 - Instructions.emplace_back(Mips::XOR, Temp, LHS, RHS); - Instructions.emplace_back(Mips::SLTiu, ICMPReg, Temp, 1); - break; - case CmpInst::ICMP_NE: // LHS != RHS -> 0 < (LHS ^ RHS) - Instructions.emplace_back(Mips::XOR, Temp, LHS, RHS); - Instructions.emplace_back(Mips::SLTu, ICMPReg, Mips::ZERO, Temp); - break; - case CmpInst::ICMP_UGT: // LHS > RHS -> RHS < LHS - Instructions.emplace_back(Mips::SLTu, ICMPReg, RHS, LHS); - break; - case CmpInst::ICMP_UGE: // LHS >= RHS -> !(LHS < RHS) - Instructions.emplace_back(Mips::SLTu, Temp, LHS, RHS); - Instructions.emplace_back(Mips::XORi, ICMPReg, Temp, 1); - break; - case CmpInst::ICMP_ULT: // LHS < RHS -> LHS < RHS - Instructions.emplace_back(Mips::SLTu, ICMPReg, LHS, RHS); - break; - case CmpInst::ICMP_ULE: // LHS <= RHS -> !(RHS < LHS) - Instructions.emplace_back(Mips::SLTu, Temp, RHS, LHS); - Instructions.emplace_back(Mips::XORi, ICMPReg, Temp, 1); - break; - case CmpInst::ICMP_SGT: // LHS > RHS -> RHS < LHS - Instructions.emplace_back(Mips::SLT, ICMPReg, RHS, LHS); - break; - case CmpInst::ICMP_SGE: // LHS >= RHS -> !(LHS < RHS) - Instructions.emplace_back(Mips::SLT, Temp, LHS, RHS); - Instructions.emplace_back(Mips::XORi, ICMPReg, Temp, 1); - break; - case CmpInst::ICMP_SLT: // LHS < RHS -> LHS < RHS - Instructions.emplace_back(Mips::SLT, ICMPReg, LHS, RHS); - break; - case CmpInst::ICMP_SLE: // LHS <= RHS -> !(RHS < LHS) - Instructions.emplace_back(Mips::SLT, Temp, RHS, LHS); - Instructions.emplace_back(Mips::XORi, ICMPReg, Temp, 1); - break; - default: - return false; - } - - MachineIRBuilder B(I); - for (const struct Instr &Instruction : Instructions) { - MachineInstrBuilder MIB = B.buildInstr( - Instruction.Opcode, {Instruction.Def}, {Instruction.LHS}); - - if (Instruction.hasImm()) - MIB.addImm(Instruction.RHS); - else - MIB.addUse(Instruction.RHS); - - if (!MIB.constrainAllUses(TII, TRI, RBI)) - return false; - } - - I.eraseFromParent(); - return true; - } case G_FCMP: { unsigned MipsFCMPCondCode; bool isLogicallyNegated; Index: llvm/test/CodeGen/Mips/GlobalISel/instruction-select/icmp.mir =================================================================== --- llvm/test/CodeGen/Mips/GlobalISel/instruction-select/icmp.mir +++ llvm/test/CodeGen/Mips/GlobalISel/instruction-select/icmp.mir @@ -13,6 +13,11 @@ define void @ult_i32() {entry: ret void} define void @ule_i32() {entry: ret void} define void @eq_ptr() {entry: ret void} + define void @ne_ptr() {entry: ret void} + define void @ugt_ptr() {entry: ret void} + define void @uge_ptr() {entry: ret void} + define void @ult_ptr() {entry: ret void} + define void @ule_ptr() {entry: ret void} ... @@ -298,3 +303,131 @@ RetRA implicit $v0 ... +--- +name: ne_ptr +alignment: 4 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + bb.1.entry: + liveins: $a0, $a1 + + ; MIPS32-LABEL: name: ne_ptr + ; MIPS32: liveins: $a0, $a1 + ; MIPS32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; MIPS32: [[XOR:%[0-9]+]]:gpr32 = XOR [[COPY]], [[COPY1]] + ; MIPS32: [[SLTu:%[0-9]+]]:gpr32 = SLTu $zero, [[XOR]] + ; MIPS32: $v0 = COPY [[SLTu]] + ; MIPS32: RetRA implicit $v0 + %0:gprb(p0) = COPY $a0 + %1:gprb(p0) = COPY $a1 + %4:gprb(s32) = G_ICMP intpred(ne), %0(p0), %1 + %3:gprb(s32) = COPY %4(s32) + $v0 = COPY %3(s32) + RetRA implicit $v0 + +... +--- +name: ugt_ptr +alignment: 4 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + bb.1.entry: + liveins: $a0, $a1 + + ; MIPS32-LABEL: name: ugt_ptr + ; MIPS32: liveins: $a0, $a1 + ; MIPS32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; MIPS32: [[SLTu:%[0-9]+]]:gpr32 = SLTu [[COPY1]], [[COPY]] + ; MIPS32: $v0 = COPY [[SLTu]] + ; MIPS32: RetRA implicit $v0 + %0:gprb(p0) = COPY $a0 + %1:gprb(p0) = COPY $a1 + %4:gprb(s32) = G_ICMP intpred(ugt), %0(p0), %1 + %3:gprb(s32) = COPY %4(s32) + $v0 = COPY %3(s32) + RetRA implicit $v0 + +... +--- +name: uge_ptr +alignment: 4 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + bb.1.entry: + liveins: $a0, $a1 + + ; MIPS32-LABEL: name: uge_ptr + ; MIPS32: liveins: $a0, $a1 + ; MIPS32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; MIPS32: [[SLTu:%[0-9]+]]:gpr32 = SLTu [[COPY]], [[COPY1]] + ; MIPS32: [[XORi:%[0-9]+]]:gpr32 = XORi [[SLTu]], 1 + ; MIPS32: $v0 = COPY [[XORi]] + ; MIPS32: RetRA implicit $v0 + %0:gprb(p0) = COPY $a0 + %1:gprb(p0) = COPY $a1 + %4:gprb(s32) = G_ICMP intpred(uge), %0(p0), %1 + %3:gprb(s32) = COPY %4(s32) + $v0 = COPY %3(s32) + RetRA implicit $v0 + +... +--- +name: ult_ptr +alignment: 4 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + bb.1.entry: + liveins: $a0, $a1 + + ; MIPS32-LABEL: name: ult_ptr + ; MIPS32: liveins: $a0, $a1 + ; MIPS32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; MIPS32: [[SLTu:%[0-9]+]]:gpr32 = SLTu [[COPY1]], [[COPY]] + ; MIPS32: $v0 = COPY [[SLTu]] + ; MIPS32: RetRA implicit $v0 + %0:gprb(p0) = COPY $a0 + %1:gprb(p0) = COPY $a1 + %4:gprb(s32) = G_ICMP intpred(ult), %0(p0), %1 + %3:gprb(s32) = COPY %4(s32) + $v0 = COPY %3(s32) + RetRA implicit $v0 + +... +--- +name: ule_ptr +alignment: 4 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + bb.1.entry: + liveins: $a0, $a1 + + ; MIPS32-LABEL: name: ule_ptr + ; MIPS32: liveins: $a0, $a1 + ; MIPS32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; MIPS32: [[SLTu:%[0-9]+]]:gpr32 = SLTu [[COPY1]], [[COPY]] + ; MIPS32: [[XORi:%[0-9]+]]:gpr32 = XORi [[SLTu]], 1 + ; MIPS32: $v0 = COPY [[XORi]] + ; MIPS32: RetRA implicit $v0 + %0:gprb(p0) = COPY $a0 + %1:gprb(p0) = COPY $a1 + %4:gprb(s32) = G_ICMP intpred(ule), %0(p0), %1 + %3:gprb(s32) = COPY %4(s32) + $v0 = COPY %3(s32) + RetRA implicit $v0 + +... Index: llvm/test/CodeGen/Mips/GlobalISel/instruction-select/icmp_nullptr.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/Mips/GlobalISel/instruction-select/icmp_nullptr.mir @@ -0,0 +1,72 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -O0 -mtriple=mipsel-linux-gnu -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s -check-prefixes=MIPS32 +--- | + + define zeroext i1 @_Z10eq_nullptrPi(i32* %a) { + entry: + %cmp = icmp eq i32* %a, null + ret i1 %cmp + } + + define zeroext i1 @_Z7eq_zeroi(i32 %a) { + entry: + %cmp = icmp eq i32 %a, 0 + ret i1 %cmp + } + +... +--- +name: _Z10eq_nullptrPi +alignment: 4 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + bb.1.entry: + liveins: $a0 + + ; MIPS32-LABEL: name: _Z10eq_nullptrPi + ; MIPS32: liveins: $a0 + ; MIPS32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32: [[SLTiu:%[0-9]+]]:gpr32 = SLTiu [[COPY]], 1 + ; MIPS32: [[ANDi:%[0-9]+]]:gpr32 = ANDi [[SLTiu]], 1 + ; MIPS32: $v0 = COPY [[ANDi]] + ; MIPS32: RetRA implicit $v0 + %0:gprb(p0) = COPY $a0 + %2:gprb(s32) = G_CONSTANT i32 0 + %1:gprb(p0) = G_INTTOPTR %2(s32) + %5:gprb(s32) = G_ICMP intpred(eq), %0(p0), %1 + %6:gprb(s32) = G_CONSTANT i32 1 + %7:gprb(s32) = COPY %5(s32) + %4:gprb(s32) = G_AND %7, %6 + $v0 = COPY %4(s32) + RetRA implicit $v0 + +... +--- +name: _Z7eq_zeroi +alignment: 4 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + bb.1.entry: + liveins: $a0 + + ; MIPS32-LABEL: name: _Z7eq_zeroi + ; MIPS32: liveins: $a0 + ; MIPS32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32: [[SLTiu:%[0-9]+]]:gpr32 = SLTiu [[COPY]], 1 + ; MIPS32: [[ANDi:%[0-9]+]]:gpr32 = ANDi [[SLTiu]], 1 + ; MIPS32: $v0 = COPY [[ANDi]] + ; MIPS32: RetRA implicit $v0 + %0:gprb(s32) = COPY $a0 + %1:gprb(s32) = G_CONSTANT i32 0 + %4:gprb(s32) = G_ICMP intpred(eq), %0(s32), %1 + %5:gprb(s32) = G_CONSTANT i32 1 + %6:gprb(s32) = COPY %4(s32) + %3:gprb(s32) = G_AND %6, %5 + $v0 = COPY %3(s32) + RetRA implicit $v0 + +... Index: llvm/test/CodeGen/Mips/GlobalISel/instruction-select/select_patterns.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/Mips/GlobalISel/instruction-select/select_patterns.mir @@ -0,0 +1,40 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -O0 -mtriple=mipsel-linux-gnu -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s -check-prefixes=MIPS32FP32 +--- | + define i32* @select(i32* %Test, i32* %T, i32* %F) { + entry: + %cmp = icmp ne i32* %Test, null + %cond = select i1 %cmp, i32* %F, i32* %T + ret i32* %cond + } + +... +--- +name: select +alignment: 4 +legalized: true +regBankSelected: true +tracksRegLiveness: true +body: | + bb.1.entry: + liveins: $a0, $a1, $a2 + + ; MIPS32FP32-LABEL: name: select + ; MIPS32FP32: liveins: $a0, $a1, $a2 + ; MIPS32FP32: [[COPY:%[0-9]+]]:gpr32 = COPY $a0 + ; MIPS32FP32: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; MIPS32FP32: [[COPY2:%[0-9]+]]:gpr32 = COPY $a2 + ; MIPS32FP32: [[MOVN_I_I:%[0-9]+]]:gpr32 = MOVN_I_I [[COPY2]], [[COPY]], [[COPY1]] + ; MIPS32FP32: $v0 = COPY [[MOVN_I_I]] + ; MIPS32FP32: RetRA implicit $v0 + %0:gprb(p0) = COPY $a0 + %1:gprb(p0) = COPY $a1 + %2:gprb(p0) = COPY $a2 + %4:gprb(s32) = G_CONSTANT i32 0 + %3:gprb(p0) = G_INTTOPTR %4(s32) + %8:gprb(s32) = G_ICMP intpred(ne), %0(p0), %3 + %6:gprb(p0) = G_SELECT %8(s32), %2, %1 + $v0 = COPY %6(p0) + RetRA implicit $v0 + +... Index: llvm/test/CodeGen/Mips/GlobalISel/irtranslator/icmp_nullptr.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/Mips/GlobalISel/irtranslator/icmp_nullptr.ll @@ -0,0 +1,33 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +; RUN: llc -O0 -mtriple=mipsel-linux-gnu -global-isel -stop-after=irtranslator -verify-machineinstrs %s -o - | FileCheck %s -check-prefixes=MIPS32 + +define zeroext i1 @_Z10eq_nullptrPi(i32* %a) { + ; MIPS32-LABEL: name: _Z10eq_nullptrPi + ; MIPS32: bb.1.entry: + ; MIPS32: liveins: $a0 + ; MIPS32: [[COPY:%[0-9]+]]:_(p0) = COPY $a0 + ; MIPS32: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0 + ; MIPS32: [[INTTOPTR:%[0-9]+]]:_(p0) = G_INTTOPTR [[C]](s32) + ; MIPS32: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), [[COPY]](p0), [[INTTOPTR]] + ; MIPS32: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[ICMP]](s1) + ; MIPS32: $v0 = COPY [[ZEXT]](s32) + ; MIPS32: RetRA implicit $v0 +entry: + %cmp = icmp eq i32* %a, null + ret i1 %cmp +} + +define zeroext i1 @_Z7eq_zeroi(i32 %a) { + ; MIPS32-LABEL: name: _Z7eq_zeroi + ; MIPS32: bb.1.entry: + ; MIPS32: liveins: $a0 + ; MIPS32: [[COPY:%[0-9]+]]:_(s32) = COPY $a0 + ; MIPS32: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0 + ; MIPS32: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), [[COPY]](s32), [[C]] + ; MIPS32: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[ICMP]](s1) + ; MIPS32: $v0 = COPY [[ZEXT]](s32) + ; MIPS32: RetRA implicit $v0 +entry: + %cmp = icmp eq i32 %a, 0 + ret i1 %cmp +} Index: llvm/test/CodeGen/Mips/GlobalISel/llvm-ir/icmp.ll =================================================================== --- llvm/test/CodeGen/Mips/GlobalISel/llvm-ir/icmp.ll +++ llvm/test/CodeGen/Mips/GlobalISel/llvm-ir/icmp.ll @@ -129,6 +129,64 @@ ret i1 %cmp } +define i1 @ne_ptr(i32* %a, i32* %b) { +; MIPS32-LABEL: ne_ptr: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: xor $1, $4, $5 +; MIPS32-NEXT: sltu $2, $zero, $1 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +entry: + %cmp = icmp ne i32* %a, %b + ret i1 %cmp +} + +define i1 @ugt_ptr(i32* %a, i32* %b) { +; MIPS32-LABEL: ugt_ptr: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: sltu $2, $5, $4 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +entry: + %cmp = icmp ugt i32* %a, %b + ret i1 %cmp +} + +define i1 @uge_ptr(i32* %a, i32* %b) { +; MIPS32-LABEL: uge_ptr: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: sltu $1, $4, $5 +; MIPS32-NEXT: xori $2, $1, 1 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +entry: + %cmp = icmp uge i32* %a, %b + ret i1 %cmp +} + +define i1 @ult_ptr(i32* %a, i32* %b) { +; MIPS32-LABEL: ult_ptr: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: sltu $2, $5, $4 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +entry: + %cmp = icmp ult i32* %a, %b + ret i1 %cmp +} + +define i1 @ule_ptr(i32* %a, i32* %b) { +; MIPS32-LABEL: ule_ptr: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: sltu $1, $5, $4 +; MIPS32-NEXT: xori $2, $1, 1 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +entry: + %cmp = icmp ule i32* %a, %b + ret i1 %cmp +} + define i1 @ult_i8(i8 %a, i8 %b) { ; MIPS32-LABEL: ult_i8: ; MIPS32: # %bb.0: # %entry Index: llvm/test/CodeGen/Mips/GlobalISel/llvm-ir/icmp_nullptr.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/Mips/GlobalISel/llvm-ir/icmp_nullptr.ll @@ -0,0 +1,25 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -O0 -mtriple=mipsel-linux-gnu -global-isel -verify-machineinstrs %s -o -| FileCheck %s -check-prefixes=MIPS32 + +define zeroext i1 @_Z10eq_nullptrPi(i32* %a) { +; MIPS32-LABEL: _Z10eq_nullptrPi: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: sltiu $1, $4, 1 +; MIPS32-NEXT: andi $2, $1, 1 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +entry: + %cmp = icmp eq i32* %a, null + ret i1 %cmp +} +define zeroext i1 @_Z7eq_zeroi(i32 %a) { +; MIPS32-LABEL: _Z7eq_zeroi: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: sltiu $1, $4, 1 +; MIPS32-NEXT: andi $2, $1, 1 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +entry: + %cmp = icmp eq i32 %a, 0 + ret i1 %cmp +} Index: llvm/test/TableGen/Common/GlobalISelEmitterCommon.td =================================================================== --- llvm/test/TableGen/Common/GlobalISelEmitterCommon.td +++ llvm/test/TableGen/Common/GlobalISelEmitterCommon.td @@ -9,6 +9,7 @@ def FPR32 : RegisterClass<"MyTarget", [f32], 32, (add F0)>; def FPR32Op : RegisterOperand; def p0 : PtrValueType ; +def p0Nullptr : NullPtrValue ; class I Pat> : Instruction { Index: llvm/test/TableGen/GlobalISelEmitter.td =================================================================== --- llvm/test/TableGen/GlobalISelEmitter.td +++ llvm/test/TableGen/GlobalISelEmitter.td @@ -815,7 +815,7 @@ // NOOPT-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src3 // NOOPT-NEXT: GIR_EraseFromParent, /*InsnID*/0, // NOOPT-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, -// NOOPT-NEXT: // GIR_Coverage, 26, +// NOOPT-NEXT: // GIR_Coverage, 27, // NOOPT-NEXT: GIR_Done, // NOOPT-NEXT: // Label [[LABEL_NUM]]: @[[LABEL]] @@ -1006,6 +1006,44 @@ def SEXTLOAD : I<(outs GPR32:$dst), (ins GPR32:$src1), [(set GPR32:$dst, (sextloadi16 GPR32:$src1))]>; +//===- Test a simple pattern with explicit nullptr operand. ---------------===// + +// NOOPT-NEXT: GIM_Try, /*On fail goto*//*Label [[LABEL_NUM:[0-9]+]]*/ [[LABEL:[0-9]+]], +// NOOPT-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/4, +// NOOPT-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_ICMP, +// NOOPT-NEXT: // MIs[0] scr +// NOOPT-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32, +// NOOPT-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, +// NOOPT-NEXT: // MIs[0] Operand 1 +// NOOPT-NEXT: GIM_CheckCmpPredicate, /*MI*/0, /*Op*/1, /*Predicate*/CmpInst::ICMP_EQ, +// NOOPT-NEXT: // MIs[0] lhs +// NOOPT-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_p0s32, +// NOOPT-NEXT: // MIs[0] Operand 3 +// NOOPT-NEXT: GIM_CheckType, /*MI*/0, /*Op*/3, /*Type*/GILLT_p0s32, +// NOOPT-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/3, // MIs[1] +// NOOPT-NEXT: GIM_CheckNumOperands, /*MI*/1, /*Expected*/2, +// NOOPT-NEXT: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_INTTOPTR, +// NOOPT-NEXT: // MIs[1] Operand 0 +// NOOPT-NEXT: GIM_CheckPointerToAny, /*MI*/1, /*Op*/0, /*SizeInBits*/32, +// NOOPT-NEXT: // MIs[1] Operand 1 +// NOOPT-NEXT: GIM_CheckType, /*MI*/1, /*Op*/1, /*Type*/GILLT_s32, +// NOOPT-NEXT: GIM_CheckConstantInt, /*MI*/1, /*Op*/1, 123, +// NOOPT-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1, +// NOOPT-NEXT: // (setcc:{ *:[i32] } p0:{ *:[i32] }:$lhs, p0Nullptr:{ *:[i32] }, SETEQ:{ *:[Other] }) => (INSN5:{ *:[i32] } GPR32:{ *:[i32] }:$lhs, 1:{ *:[i32] }) +// NOOPT-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::INSN5, +// NOOPT-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // scr +// NOOPT-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/2, // lhs +// NOOPT-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/1, +// NOOPT-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// NOOPT-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, +// NOOPT-NEXT: // GIR_Coverage, 24, +// NOOPT-NEXT: GIR_Done, +// NOOPT-NEXT: // Label [[LABEL_NUM]]: @[[LABEL]] + +def INSN5 : I<(outs GPR32:$scr), (ins GPR32:$src3, i32imm:$src5a), []>; +def : Pat<(seteq p0:$lhs, p0Nullptr), + (INSN5 GPR32:$lhs, 1)>; + //===- Test a simple pattern with regclass operands. ----------------------===// // // NOOPT-NEXT: GIM_Try, /*On fail goto*//*Label [[LABEL_NUM:[0-9]+]]*/ [[LABEL:[0-9]+]], @@ -1070,7 +1108,7 @@ // NOOPT-NEXT: // (add:{ *:[i32] } i32:{ *:[i32] }:$src1, i32:{ *:[i32] }:$src2) => (ADD:{ *:[i32] } i32:{ *:[i32] }:$src1, i32:{ *:[i32] }:$src2) // NOOPT-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::ADD, // NOOPT-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, -// NOOPT-NEXT: // GIR_Coverage, 24, +// NOOPT-NEXT: // GIR_Coverage, 25, // NOOPT-NEXT: GIR_Done, // NOOPT-NEXT: // Label [[LABEL_NUM]]: @[[LABEL]] @@ -1122,7 +1160,7 @@ // NOOPT-NEXT: // (bitconvert:{ *:[i32] } FPR32:{ *:[f32] }:$src1) => (COPY_TO_REGCLASS:{ *:[i32] } FPR32:{ *:[f32] }:$src1, GPR32:{ *:[i32] }) // NOOPT-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/TargetOpcode::COPY, // NOOPT-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/0, /*RC GPR32*/1, -// NOOPT-NEXT: // GIR_Coverage, 25, +// NOOPT-NEXT: // GIR_Coverage, 26, // NOOPT-NEXT: GIR_Done, // NOOPT-NEXT: // Label [[LABEL_NUM]]: @[[LABEL]] Index: llvm/utils/TableGen/CodeGenDAGPatterns.cpp =================================================================== --- llvm/utils/TableGen/CodeGenDAGPatterns.cpp +++ llvm/utils/TableGen/CodeGenDAGPatterns.cpp @@ -2172,6 +2172,12 @@ return TypeSetByHwMode(MVT::i32); } + if (R->isSubClassOf("NullPtrValue")) { + assert (Unnamed && "Named NullPtrValue"); + const CodeGenHwModes &CGH = CDP.getTargetInfo().getHwModes(); + return TypeSetByHwMode(getValueTypeByHwMode(R, CGH)); + } + if (R->isSubClassOf("ValueType")) { assert(ResNo == 0 && "This node only has one result!"); // An unnamed VTSDNode represents itself as an MVT::Other immediate. Index: llvm/utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- llvm/utils/TableGen/GlobalISelEmitter.cpp +++ llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -3903,6 +3903,31 @@ return Error::success(); } + // Check for nullptr ValueType. + if (ChildRec->isSubClassOf("NullPtrValue")) { + auto IntToPtrDefOM = OM.addPredicate( + InsnMatcher.getRuleMatcher(), ""); + InstructionMatcher &IntToPtrInsnMatcher = + (*IntToPtrDefOM)->getInsnMatcher(); + IntToPtrInsnMatcher.addPredicate( + &Target.getInstruction(RK.getDef("G_INTTOPTR"))); + + OperandMatcher &OM0 = IntToPtrInsnMatcher.addOperand(0, "", TempOpIdx); + if (auto Error = OM0.addTypeCheckPredicate(ChildTypes.front(), true)) + return failedImport(toString(std::move(Error)) + + " for result of Src pattern operator"); + + OperandMatcher &OM1 = IntToPtrInsnMatcher.addOperand(1, "", TempOpIdx); + TypeSetByHwMode IntPtrTy( + (MVT::SimpleValueType)ChildRec->getValueAsInt("Value")); + if (auto Error = OM1.addTypeCheckPredicate(IntPtrTy, false)) + return failedImport(toString(std::move(Error)) + + " for result of Src pattern operator"); + OM1.addPredicate( + ChildRec->getValueAsInt("IntValue")); + return Error::success(); + } + // Check for ValueType. if (ChildRec->isSubClassOf("ValueType")) { // We already added a type check as standard practice so this doesn't need