Index: llvm/test/TableGen/GlobalISelEmitter-multiple-output.td =================================================================== --- llvm/test/TableGen/GlobalISelEmitter-multiple-output.td +++ llvm/test/TableGen/GlobalISelEmitter-multiple-output.td @@ -10,6 +10,7 @@ // Verify that patterns with multiple outputs are translated +//----------------------------------------------------------------------------- // Test where only the opcode is mutated during ISel let Constraints = "$ptr_out = $addr" in @@ -46,6 +47,7 @@ // CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::LDPost, // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, +//----------------------------------------------------------------------------- // Test where a whole new MIR instruction is created during ISel def TWO_INS : I<(outs GPR32:$out1, GPR32:$out2), (ins GPR32:$in1, GPR32:$in2), []>; @@ -84,3 +86,34 @@ // CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/2, // i1 // CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, + +//----------------------------------------------------------------------------- +// Test where implicit defs are added using Defs. + +let Defs = [R0] in +def ImplicitDefInstr : I<(outs GPR32:$dst), (ins GPR32:$src), []>; +def OtherInstr : I<(outs GPR32:$dst), (ins GPR32:$src), []>; + +def : Pat<(i32 (add i32:$src, i32:$src)), + (OtherInstr (ImplicitDefInstr GPR32:$src))>; + +// CHECK: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_ADD, +// CHECK-NEXT: // MIs[0] dst +// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32, +// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, +// CHECK-NEXT: // MIs[0] src +// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32, +// CHECK-NEXT: // MIs[0] src +// CHECK-NEXT: GIM_CheckIsSameOperand, /*MI*/0, /*OpIdx*/2, /*OtherMI*/0, /*OtherOpIdx*/1, +// CHECK-NEXT: // (add:{ *:[i32] } i32:{ *:[i32] }:$src, i32:{ *:[i32] }:$src) => (OtherInstr:{ *:[i32] } (ImplicitDefInstr:{ *:[i32] }:{ *:[i32] } GPR32:{ *:[i32] }:$src)) +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/MyTarget::ImplicitDefInstr, +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // src +// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/1, /*OpIdx for MyTarget::R0*/0, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/1, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::OtherInstr, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0, +// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, Index: llvm/utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- llvm/utils/TableGen/GlobalISelEmitter.cpp +++ llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -286,9 +286,11 @@ static Expected getInstResultType(const TreePatternNode *Dst) { ArrayRef ChildTypes = Dst->getExtTypes(); - if (ChildTypes.size() != 1) - return failedImport("Dst pattern child has multiple results"); + if (ChildTypes.size() < 1) + return failedImport("Dst pattern child has no result"); + // If there are multiple results, just take the first one (this is how + // SelectionDAG does it). std::optional MaybeOpTy; if (ChildTypes.front().isMachineValueType()) { MaybeOpTy = MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy); @@ -1776,10 +1778,11 @@ return getRegClassFromLeaf(N); // We don't have a leaf node, so we have to try and infer something. Check - // that we have an instruction that we an infer something from. + // that we have an instruction that we can infer something from. - // Only handle things that produce a single type. - if (N->getNumTypes() != 1) + // Only handle things that produce at least one value (if multiple values, + // just take the first one). + if (N->getNumTypes() < 1) return std::nullopt; Record *OpRec = N->getOperator();