Index: include/llvm/CodeGen/MachineInstr.h =================================================================== --- include/llvm/CodeGen/MachineInstr.h +++ include/llvm/CodeGen/MachineInstr.h @@ -792,7 +792,10 @@ && getOperand(1).isImm(); } - bool isPHI() const { return getOpcode() == TargetOpcode::PHI; } + bool isPHI() const { + return getOpcode() == TargetOpcode::PHI || + getOpcode() == TargetOpcode::G_PHI; + } bool isKill() const { return getOpcode() == TargetOpcode::KILL; } bool isImplicitDef() const { return getOpcode()==TargetOpcode::IMPLICIT_DEF; } bool isInlineAsm() const { return getOpcode() == TargetOpcode::INLINEASM; } @@ -869,6 +872,7 @@ return isMetaInstruction(); // Copy-like instructions are usually eliminated during register allocation. case TargetOpcode::PHI: + case TargetOpcode::G_PHI: case TargetOpcode::COPY: case TargetOpcode::INSERT_SUBREG: case TargetOpcode::SUBREG_TO_REG: Index: include/llvm/Target/GenericOpcodes.td =================================================================== --- include/llvm/Target/GenericOpcodes.td +++ include/llvm/Target/GenericOpcodes.td @@ -55,6 +55,12 @@ let hasSideEffects = 0; } +def G_PHI : Instruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins variable_ops); + let hasSideEffects = 0; +} + def G_FRAME_INDEX : Instruction { let OutOperandList = (outs type0:$dst); let InOperandList = (ins unknown:$src2); Index: include/llvm/Target/TargetOpcodes.def =================================================================== --- include/llvm/Target/TargetOpcodes.def +++ include/llvm/Target/TargetOpcodes.def @@ -224,6 +224,9 @@ HANDLE_TARGET_OPCODE(G_IMPLICIT_DEF) +/// Generic PHI instruction with types. +HANDLE_TARGET_OPCODE(G_PHI) + /// Generic instruction to materialize the address of an alloca or other /// stack-based object. HANDLE_TARGET_OPCODE(G_FRAME_INDEX) Index: lib/CodeGen/GlobalISel/IRTranslator.cpp =================================================================== --- lib/CodeGen/GlobalISel/IRTranslator.cpp +++ lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -1098,7 +1098,7 @@ bool IRTranslator::translatePHI(const User &U, MachineIRBuilder &MIRBuilder) { const PHINode &PI = cast(U); - auto MIB = MIRBuilder.buildInstr(TargetOpcode::PHI); + auto MIB = MIRBuilder.buildInstr(TargetOpcode::G_PHI); MIB.addDef(getOrCreateVReg(PI)); PendingPHIs.emplace_back(&PI, MIB.getInstr()); Index: lib/CodeGen/MachineVerifier.cpp =================================================================== --- lib/CodeGen/MachineVerifier.cpp +++ lib/CodeGen/MachineVerifier.cpp @@ -926,6 +926,23 @@ report("Generic instruction accessing memory must have one mem operand", MI); break; + case TargetOpcode::G_PHI: { + LLT DstTy = MRI->getType(MI->getOperand(0).getReg()); + if (!DstTy.isValid() || + !std::all_of(MI->operands_begin() + 1, MI->operands_end(), + [this, &DstTy](const MachineOperand &MO) { + if (!MO.isReg()) + return true; + LLT Ty = MRI->getType(MO.getReg()); + if (!Ty.isValid() || (Ty != DstTy)) + return false; + return true; + })) + report("Generic Instruction G_PHI has operands with incompatible/missing " + "types", + MI); + break; + } case TargetOpcode::STATEPOINT: if (!MI->getOperand(StatepointOpers::IDPos).isImm() || !MI->getOperand(StatepointOpers::NBytesPos).isImm() || Index: lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- lib/Target/AArch64/AArch64InstructionSelector.cpp +++ lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -590,13 +590,14 @@ MachineRegisterInfo &MRI = MF.getRegInfo(); unsigned Opcode = I.getOpcode(); - if (!isPreISelGenericOpcode(I.getOpcode())) { + // G_PHI requires same handling as PHI + if (!isPreISelGenericOpcode(Opcode) || Opcode == TargetOpcode::G_PHI) { // Certain non-generic instructions also need some special handling. if (Opcode == TargetOpcode::LOAD_STACK_GUARD) return constrainSelectedInstRegOperands(I, TII, TRI, RBI); - if (Opcode == TargetOpcode::PHI) { + if (Opcode == TargetOpcode::PHI || Opcode == TargetOpcode::G_PHI) { const unsigned DefReg = I.getOperand(0).getReg(); const LLT DefTy = MRI.getType(DefReg); @@ -621,6 +622,7 @@ } } } + I.setDesc(TII.get(TargetOpcode::PHI)); return RBI.constrainGenericRegister(DefReg, *DefRC, MRI); } Index: lib/Target/AArch64/AArch64LegalizerInfo.cpp =================================================================== --- lib/Target/AArch64/AArch64LegalizerInfo.cpp +++ lib/Target/AArch64/AArch64LegalizerInfo.cpp @@ -38,6 +38,9 @@ for (auto Ty : {p0, s1, s8, s16, s32, s64}) setAction({G_IMPLICIT_DEF, Ty}, Legal); + for (auto Ty : {s16, s32, s64}) + setAction({G_PHI, Ty}, Legal); + for (unsigned BinOp : {G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR, G_SHL}) { // These operations naturally get the right answer when used on // GPR32, even if the actual type is narrower. Index: lib/Target/AArch64/AArch64RegisterBankInfo.cpp =================================================================== --- lib/Target/AArch64/AArch64RegisterBankInfo.cpp +++ lib/Target/AArch64/AArch64RegisterBankInfo.cpp @@ -420,7 +420,8 @@ // Try the default logic for non-generic instructions that are either copies // or already have some operands assigned to banks. - if (!isPreISelGenericOpcode(Opc)) { + if (!isPreISelGenericOpcode(Opc) || + Opc == TargetOpcode::G_PHI) { const RegisterBankInfo::InstructionMapping &Mapping = getInstrMappingImpl(MI); if (Mapping.isValid()) Index: lib/Target/ARM/ARMRegisterBankInfo.cpp =================================================================== --- lib/Target/ARM/ARMRegisterBankInfo.cpp +++ lib/Target/ARM/ARMRegisterBankInfo.cpp @@ -198,7 +198,7 @@ // Try the default logic for non-generic instructions that are either copies // or already have some operands assigned to banks. - if (!isPreISelGenericOpcode(Opc)) { + if (!isPreISelGenericOpcode(Opc) || Opc == TargetOpcode::G_PHI) { const InstructionMapping &Mapping = getInstrMappingImpl(MI); if (Mapping.isValid()) return Mapping; Index: lib/Target/X86/X86RegisterBankInfo.cpp =================================================================== --- lib/Target/X86/X86RegisterBankInfo.cpp +++ lib/Target/X86/X86RegisterBankInfo.cpp @@ -160,7 +160,7 @@ // Try the default logic for non-generic instructions that are either copies // or already have some operands assigned to banks. - if (!isPreISelGenericOpcode(Opc)) { + if (!isPreISelGenericOpcode(Opc) || Opc == TargetOpcode::G_PHI) { const InstructionMapping &Mapping = getInstrMappingImpl(MI); if (Mapping.isValid()) return Mapping; Index: test/CodeGen/AArch64/GlobalISel/arm64-irtranslator.ll =================================================================== --- test/CodeGen/AArch64/GlobalISel/arm64-irtranslator.ll +++ test/CodeGen/AArch64/GlobalISel/arm64-irtranslator.ll @@ -170,7 +170,7 @@ ; CHECK: %[[regretc200:[0-9]+]](s32) = G_ADD %0, %[[reg2]] ; ; CHECK: [[BB_RET]]: -; CHECK-NEXT: %[[regret:[0-9]+]](s32) = PHI %[[regretdefault]](s32), %[[BB_DEFAULT]], %[[regretc100]](s32), %[[BB_CASE100]] +; CHECK-NEXT: %[[regret:[0-9]+]](s32) = G_PHI %[[regretdefault]](s32), %[[BB_DEFAULT]], %[[regretc100]](s32), %[[BB_CASE100]] ; CHECK: %w0 = COPY %[[regret]](s32) ; CHECK: RET_ReallyLR implicit %w0 ; @@ -211,7 +211,7 @@ ; CHECK: G_BR %[[PHI_BLOCK]] ; ; CHECK: [[PHI_BLOCK]]: -; CHECK-NEXT: PHI %{{.*}}(s32), %[[NOTCASE57_BLOCK:bb.[0-9]+.entry]], %{{.*}}(s32), +; CHECK-NEXT: G_PHI %{{.*}}(s32), %[[NOTCASE57_BLOCK:bb.[0-9]+.entry]], %{{.*}}(s32), ; define i32 @test_cfg_remap(i32 %in) { entry: @@ -230,7 +230,7 @@ } ; CHECK-LABEL: name: test_cfg_remap_multiple_preds -; CHECK: PHI [[ENTRY:%.*]](s32), %bb.{{[0-9]+}}.entry, [[ENTRY]](s32), %bb.{{[0-9]+}}.entry +; CHECK: G_PHI [[ENTRY:%.*]](s32), %bb.{{[0-9]+}}.entry, [[ENTRY]](s32), %bb.{{[0-9]+}}.entry define i32 @test_cfg_remap_multiple_preds(i32 %in) { entry: switch i32 %in, label %odd [i32 1, label %next @@ -521,7 +521,7 @@ ; CHECK: [[FALSE]]: ; CHECK: [[RES2:%[0-9]+]](s32) = G_LOAD -; CHECK: [[RES:%[0-9]+]](s32) = PHI [[RES1]](s32), %[[TRUE]], [[RES2]](s32), %[[FALSE]] +; CHECK: [[RES:%[0-9]+]](s32) = G_PHI [[RES1]](s32), %[[TRUE]], [[RES2]](s32), %[[FALSE]] ; CHECK: %w0 = COPY [[RES]] define i32 @test_phi(i32* %addr1, i32* %addr2, i1 %tst) { br i1 %tst, label %true, label %false Index: test/CodeGen/AArch64/GlobalISel/select-phi.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/select-phi.mir @@ -0,0 +1,78 @@ +# RUN: llc -mtriple=aarch64-unknown-unknown -o - -global-isel -verify-machineinstrs -run-pass=instruction-select %s | FileCheck %s +--- | + ; ModuleID = '/tmp/test.ll' + source_filename = "/tmp/test.ll" + target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-unknown-unknown" + + define i32 @test_phi(i32 %argc) { + entry: + %cmp = icmp ugt i32 %argc, 0 + br i1 %cmp, label %case1, label %case2 + + case1: ; preds = %entry + %tmp1 = add i32 %argc, 1 + br label %return + + case2: ; preds = %entry + %tmp2 = add i32 %argc, 2 + br label %return + + return: ; preds = %case2, %case1 + %res = phi i32 [ %tmp1, %case1 ], [ %tmp2, %case2 ] + ret i32 %res + } + +... +--- +name: test_phi +alignment: 2 +exposesReturnsTwice: false +legalized: true +regBankSelected: true +selected: false +tracksRegLiveness: true +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: gpr, preferred-register: '' } + - { id: 4, class: gpr, preferred-register: '' } + - { id: 5, class: gpr, preferred-register: '' } + - { id: 6, class: gpr, preferred-register: '' } + - { id: 7, class: gpr, preferred-register: '' } + - { id: 8, class: gpr, preferred-register: '' } +liveins: +body: | + bb.1.entry: + successors: %bb.2.case1(0x40000000), %bb.3.case2(0x40000000) + liveins: %w0 + ; CHECK-LABEL: name: test_phi + ; CHECK: [[RES:%.*]] = PHI + + %0(s32) = COPY %w0 + %1(s32) = G_CONSTANT i32 0 + %3(s32) = G_CONSTANT i32 1 + %5(s32) = G_CONSTANT i32 2 + %8(s32) = G_ICMP intpred(ugt), %0(s32), %1 + %2(s1) = G_TRUNC %8(s32) + G_BRCOND %2(s1), %bb.2.case1 + G_BR %bb.3.case2 + + bb.2.case1: + successors: %bb.4.return(0x80000000) + + %4(s32) = G_ADD %0, %3 + G_BR %bb.4.return + + bb.3.case2: + successors: %bb.4.return(0x80000000) + + %6(s32) = G_ADD %0, %5 + + bb.4.return: + %7(s32) = G_PHI %4(s32), %bb.2.case1, %6(s32), %bb.3.case2 + %w0 = COPY %7(s32) + RET_ReallyLR implicit %w0 + +... Index: test/Verifier/test_g_phi.mir =================================================================== --- /dev/null +++ test/Verifier/test_g_phi.mir @@ -0,0 +1,78 @@ +#RUN: not llc -mtriple=aarch64-unknown-unknown -o - -global-isel -run-pass=legalizer -verify-machineinstrs %s 2>&1 | FileCheck %s +--- | + ; ModuleID = 'test.ll' + source_filename = "test.ll" + target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-unknown-unknown" + + define i32 @test_phi(i32 %argc) { + entry: + %cmp = icmp ugt i32 %argc, 0 + br i1 %cmp, label %case1, label %case2 + + case1: ; preds = %entry + %tmp11 = add i32 %argc, 1 + br label %return + + case2: ; preds = %entry + %tmp22 = add i32 %argc, 2 + br label %return + + return: ; preds = %case2, %case1 + %res = phi i32 [ %tmp11, %case1 ], [ %tmp22, %case2 ] + ret i32 %res + } + +... +--- +name: test_phi +legalized: true +regBankSelected: false +selected: false +tracksRegLiveness: true +registers: + - { id: 0, class: _, preferred-register: '' } + - { id: 1, class: _, preferred-register: '' } + - { id: 2, class: _, preferred-register: '' } + - { id: 3, class: _, preferred-register: '' } + - { id: 4, class: _, preferred-register: '' } + - { id: 5, class: _, preferred-register: '' } + - { id: 6, class: _, preferred-register: '' } + - { id: 7, class: _, preferred-register: '' } + - { id: 8, class: _, preferred-register: '' } + - { id: 9, class: _, preferred-register: '' } +liveins: +body: | + bb.1.entry: + successors: %bb.2.case1(0x40000000), %bb.3.case2(0x40000000) + liveins: %w0 + ; This test makes sure that the Verifier catches G_PHI with mismatching types. + ; CHECK: Bad machine code: Generic Instruction G_PHI has operands with incompatible/missing types + + %0(s32) = COPY %w0 + %1(s32) = G_CONSTANT i32 0 + %3(s32) = G_CONSTANT i32 1 + %5(s32) = G_CONSTANT i32 2 + %8(s32) = G_ICMP intpred(ugt), %0(s32), %1 + %2(s1) = G_TRUNC %8(s32) + G_BRCOND %2(s1), %bb.2.case1 + G_BR %bb.3.case2 + + bb.2.case1: + successors: %bb.4.return(0x80000000) + + %4(s32) = G_ADD %0, %3 + %9(s16) = G_TRUNC %4(s32) + G_BR %bb.4.return + + bb.3.case2: + successors: %bb.4.return(0x80000000) + + %6(s32) = G_ADD %0, %5 + + bb.4.return: + %7(s32) = G_PHI %9(s16), %bb.2.case1, %6(s32), %bb.3.case2 + %w0 = COPY %7(s32) + RET_ReallyLR implicit %w0 + +...