Index: include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h =================================================================== --- include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -84,6 +84,11 @@ addUseFromArg(MIB, Arg1); addUsesFromArgs(MIB, std::forward(Args)...); } + unsigned getRegFromArg(unsigned Reg) { return Reg; } + unsigned getRegFromArg(const MachineInstrBuilder &MIB) { + return MIB->getOperand(0).getReg(); + } + public: /// Some constructors for easy use. MachineIRBuilder() = default; @@ -372,7 +377,12 @@ /// \pre \p Op must be smaller than \p Res /// /// \return The newly created instruction. + MachineInstrBuilder buildAnyExt(unsigned Res, unsigned Op); + template + MachineInstrBuilder buildAnyExt(DstType &&Res, ArgType &&Arg) { + return buildAnyExt(getDestFromArg(Res), getRegFromArg(Arg)); + } /// Build and insert \p Res = G_SEXT \p Op /// @@ -422,6 +432,32 @@ /// \return The newly created instruction. MachineInstrBuilder buildZExtOrTrunc(unsigned Res, unsigned Op); + // Build and insert \p Res = G_ANYEXT \p Op, \p Res = G_TRUNC \p Op, or + /// \p Res = COPY \p Op depending on the differing sizes of \p Res and \p Op. + /// /// + /// \pre setBasicBlock or setMI must have been called. + /// \pre \p Res must be a generic virtual register with scalar or vector type. + /// \pre \p Op must be a generic virtual register with scalar or vector type. + /// + /// \return The newly created instruction. + template + MachineInstrBuilder buildAnyExtOrTrunc(DstTy &&Dst, UseArgTy &&Use) { + return buildAnyExtOrTrunc(getDestFromArg(Dst), getRegFromArg(Use)); + } + MachineInstrBuilder buildAnyExtOrTrunc(unsigned Res, unsigned Op); + + /// Build and insert \p Res = \p ExtOpc, \p Res = G_TRUNC \p + /// Op, or \p Res = COPY \p Op depending on the differing sizes of \p Res and + /// \p Op. + /// /// + /// \pre setBasicBlock or setMI must have been called. + /// \pre \p Res must be a generic virtual register with scalar or vector type. + /// \pre \p Op must be a generic virtual register with scalar or vector type. + /// + /// \return The newly created instruction. + MachineInstrBuilder buildExtOrTrunc(unsigned ExtOpc, unsigned Res, + unsigned Op); + /// Build and insert an appropriate cast between two registers of equal size. MachineInstrBuilder buildCast(unsigned Dst, unsigned Src); Index: lib/CodeGen/GlobalISel/LegalizerHelper.cpp =================================================================== --- lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -659,6 +659,36 @@ MI.getOperand(2).setReg(OffsetExt); return Legalized; } + case TargetOpcode::G_PHI: { + assert(TypeIdx == 0 && "Expecting only Idx 0"); + MachineFunction *MF = MI.getParent()->getParent(); + auto getExtendedReg = [this, MF, WideTy](unsigned Reg, + MachineBasicBlock &MBB) { + auto FirstTermIt = MBB.getFirstTerminator(); + MIRBuilder.setInsertPt(MBB, FirstTermIt); + MachineInstr *DefMI = MRI.getVRegDef(Reg); + MachineInstrBuilder MIB; + if (DefMI->getOpcode() == TargetOpcode::G_TRUNC) + MIB = MIRBuilder.buildAnyExtOrTrunc(WideTy, + DefMI->getOperand(1).getReg()); + else + MIB = MIRBuilder.buildAnyExt(WideTy, Reg); + return MIB->getOperand(0).getReg(); + }; + auto MIB = MIRBuilder.buildInstr(TargetOpcode::G_PHI, WideTy); + for (auto OpIt = MI.operands_begin() + 1, OpE = MI.operands_end(); + OpIt != OpE;) { + unsigned Reg = OpIt++->getReg(); + MachineBasicBlock *OpMBB = OpIt++->getMBB(); + MIB.addReg(getExtendedReg(Reg, *OpMBB)); + MIB.addMBB(OpMBB); + } + MIRBuilder.setInstr(MI); + MIRBuilder.buildTrunc(MI.getOperand(0).getReg(), + MIB->getOperand(0).getReg()); + MI.eraseFromParent(); + return Legalized; + } } } Index: lib/CodeGen/GlobalISel/MachineIRBuilder.cpp =================================================================== --- lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -346,14 +346,17 @@ return buildInstr(TargetOpcode::G_ZEXT).addDef(Res).addUse(Op); } -MachineInstrBuilder MachineIRBuilder::buildSExtOrTrunc(unsigned Res, - unsigned Op) { +MachineInstrBuilder +MachineIRBuilder::buildExtOrTrunc(unsigned ExtOpc, unsigned Res, unsigned Op) { + assert((TargetOpcode::G_ANYEXT == ExtOpc || TargetOpcode::G_ZEXT == ExtOpc || + TargetOpcode::G_SEXT == ExtOpc) && + "Expecting Extending Opc"); assert(MRI->getType(Res).isScalar() || MRI->getType(Res).isVector()); assert(MRI->getType(Res).isScalar() == MRI->getType(Op).isScalar()); unsigned Opcode = TargetOpcode::COPY; if (MRI->getType(Res).getSizeInBits() > MRI->getType(Op).getSizeInBits()) - Opcode = TargetOpcode::G_SEXT; + Opcode = ExtOpc; else if (MRI->getType(Res).getSizeInBits() < MRI->getType(Op).getSizeInBits()) Opcode = TargetOpcode::G_TRUNC; else @@ -362,20 +365,19 @@ return buildInstr(Opcode).addDef(Res).addUse(Op); } -MachineInstrBuilder MachineIRBuilder::buildZExtOrTrunc(unsigned Res, +MachineInstrBuilder MachineIRBuilder::buildSExtOrTrunc(unsigned Res, unsigned Op) { - assert(MRI->getType(Res).isScalar() || MRI->getType(Res).isVector()); - assert(MRI->getType(Res).isScalar() == MRI->getType(Op).isScalar()); + return buildExtOrTrunc(TargetOpcode::G_SEXT, Res, Op); +} - unsigned Opcode = TargetOpcode::COPY; - if (MRI->getType(Res).getSizeInBits() > MRI->getType(Op).getSizeInBits()) - Opcode = TargetOpcode::G_ZEXT; - else if (MRI->getType(Res).getSizeInBits() < MRI->getType(Op).getSizeInBits()) - Opcode = TargetOpcode::G_TRUNC; - else - assert(MRI->getType(Res) == MRI->getType(Op)); +MachineInstrBuilder MachineIRBuilder::buildZExtOrTrunc(unsigned Res, + unsigned Op) { + return buildExtOrTrunc(TargetOpcode::G_ZEXT, Res, Op); +} - return buildInstr(Opcode).addDef(Res).addUse(Op); +MachineInstrBuilder MachineIRBuilder::buildAnyExtOrTrunc(unsigned Res, + unsigned Op) { + return buildExtOrTrunc(TargetOpcode::G_ANYEXT, Res, Op); } MachineInstrBuilder MachineIRBuilder::buildCast(unsigned Dst, unsigned Src) { Index: lib/Target/AArch64/AArch64LegalizerInfo.cpp =================================================================== --- lib/Target/AArch64/AArch64LegalizerInfo.cpp +++ lib/Target/AArch64/AArch64LegalizerInfo.cpp @@ -41,6 +41,9 @@ for (auto Ty : {s16, s32, s64}) setAction({G_PHI, Ty}, Legal); + for (auto Ty : {s1, s8}) + setAction({G_PHI, Ty}, WidenScalar); + 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: test/CodeGen/AArch64/GlobalISel/legalize-phi.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/legalize-phi.mir @@ -0,0 +1,311 @@ +# RUN: llc -mtriple=aarch64-unknown-unknown -global-isel -verify-machineinstrs -run-pass=legalizer %s -o - | 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 @legalize_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 + %tmp1 = trunc i32 %tmp11 to i1 + br label %return + + case2: ; preds = %entry + %tmp22 = add i32 %argc, 2 + %tmp2 = trunc i32 %tmp22 to i1 + br label %return + + return: ; preds = %case2, %case1 + %res = phi i1 [ %tmp1, %case1 ], [ %tmp2, %case2 ] + %res_ext = zext i1 %res to i32 + ret i32 %res_ext + } + + define i32 @legalize_phi_empty(i32 %argc) { + entry: + %cmp = icmp ugt i32 %argc, 0 + %tmp00 = add i32 %argc, 3 + %trunc0 = trunc i32 %tmp00 to i1 + br i1 %cmp, label %case1, label %case2 + + case1: ; preds = %entry + %tmp11 = add i32 %argc, 1 + %trunc1 = trunc i32 %tmp11 to i1 + br label %return + + case2: ; preds = %entry + br label %return + + return: ; preds = %case2, %case1 + %res = phi i1 [ %trunc1, %case1 ], [ %trunc0, %case2 ] + %res_ext = zext i1 %res to i32 + ret i32 %res_ext + } + + define i32 @legalize_phi_loop(i32 %argc) { + entry: + br label %loop_body + + loop_body: ; preds = %loop_body, %entry + %counter = phi i8 [ 0, %entry ], [ %inc, %loop_body ] + %inc = add i8 %counter, 1 + %inc_ext = zext i8 %inc to i32 + %cmp = icmp ugt i32 %inc_ext, %argc + br i1 %cmp, label %loop_body, label %return + + return: ; preds = %loop_body + %res_ext = zext i8 %inc to i32 + ret i32 %res_ext + } + + define i32 @legalize_phi_cycle(i32 %argc) { + entry: + br label %loop_body + + loop_body: ; preds = %loop_body, %entry + %counter = phi i8 [ 0, %entry ], [ %counter, %loop_body ] + %counter_ext = zext i8 %counter to i32 + %cmp = icmp ugt i32 %counter_ext, %argc + br i1 %cmp, label %loop_body, label %return + + return: ; preds = %loop_body + ret i32 %counter_ext + } +... +--- +name: legalize_phi +alignment: 2 +exposesReturnsTwice: false +legalized: false +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: '' } + - { id: 10, class: _, preferred-register: '' } +liveins: +body: | + bb.1.entry: + ; Test that we insert legalization artifacts(Truncs here) into the correct BBs + ; while legalizing the G_PHI to s16. + ; CHECK-LABEL: name: legalize_phi + ; CHECK-LABEL: bb.1.case1: + ; CHECK: [[ADD_BB1:%.*]](s32) = G_ADD + ; CHECK: [[RES_BB1:%.*]](s16) = G_TRUNC [[ADD_BB1]] + + ; CHECK-LABEL: bb.2.case2: + ; CHECK: [[ADD_BB2:%.*]](s32) = G_ADD + ; CHECK: [[RES_BB2:%.*]](s16) = G_TRUNC [[ADD_BB2]] + + ; CHECK-LABEL: bb.3.return: + ; CHECK: [[RES_PHI:%.*]](s16) = G_PHI [[RES_BB1]](s16), %bb.1.case1, [[RES_BB2]](s16), %bb.2.case2 + ; CHECK: [[RES:%.*]](s1) = G_TRUNC [[RES_PHI]] + successors: %bb.2.case1(0x40000000), %bb.3.case2(0x40000000) + liveins: %w0 + + %0(s32) = COPY %w0 + %1(s32) = G_CONSTANT i32 0 + %3(s32) = G_CONSTANT i32 1 + %6(s32) = G_CONSTANT i32 2 + %2(s1) = G_ICMP intpred(ugt), %0(s32), %1 + 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 + %5(s1) = G_TRUNC %4(s32) + G_BR %bb.4.return + + bb.3.case2: + successors: %bb.4.return(0x80000000) + + %7(s32) = G_ADD %0, %6 + %8(s1) = G_TRUNC %7(s32) + + bb.4.return: + %9(s1) = G_PHI %5(s1), %bb.2.case1, %8(s1), %bb.3.case2 + %10(s32) = G_ZEXT %9(s1) + %w0 = COPY %10(s32) + RET_ReallyLR implicit %w0 + +... +--- +name: legalize_phi_empty +alignment: 2 +exposesReturnsTwice: false +legalized: false +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: '' } + - { id: 10, class: _, preferred-register: '' } +liveins: +body: | + bb.1.entry: + successors: %bb.2.case1(0x40000000), %bb.3.case2(0x40000000) + liveins: %w0 + ; Test that we properly legalize a phi with a predecessor that's empty + ; CHECK-LABEL: name: legalize_phi_empty + ; CHECK-LABEL: bb.0.entry: + ; CHECK: [[ENTRY_ADD:%.*]](s32) = G_ADD + + ; CHECK-LABEL: bb.1.case1: + ; CHECK: [[ADD_BB1:%.*]](s32) = G_ADD + ; CHECK: [[RES_BB1:%.*]](s16) = G_TRUNC [[ADD_BB1]] + + ; CHECK-LABEL: bb.2.case2: + ; CHECK: [[RES_BB2:%.*]](s16) = G_TRUNC [[ENTRY_ADD]] + + ; CHECK: [[RES_PHI:%.*]](s16) = G_PHI [[RES_BB1]](s16), %bb.1.case1, [[RES_BB2]](s16), %bb.2.case2 + ; CHECK: [[RES:%.*]](s1) = G_TRUNC [[RES_PHI]] + + %0(s32) = COPY %w0 + %1(s32) = G_CONSTANT i32 0 + %3(s32) = G_CONSTANT i32 3 + %6(s32) = G_CONSTANT i32 1 + %2(s1) = G_ICMP intpred(ugt), %0(s32), %1 + %4(s32) = G_ADD %0, %3 + %5(s1) = G_TRUNC %4(s32) + G_BRCOND %2(s1), %bb.2.case1 + G_BR %bb.3.case2 + + bb.2.case1: + successors: %bb.4.return(0x80000000) + + %7(s32) = G_ADD %0, %6 + %8(s1) = G_TRUNC %7(s32) + G_BR %bb.4.return + + bb.3.case2: + successors: %bb.4.return(0x80000000) + + + bb.4.return: + %9(s1) = G_PHI %8(s1), %bb.2.case1, %5(s1), %bb.3.case2 + %10(s32) = G_ZEXT %9(s1) + %w0 = COPY %10(s32) + RET_ReallyLR implicit %w0 + +... +--- +name: legalize_phi_loop +alignment: 2 +exposesReturnsTwice: false +legalized: false +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: '' } +liveins: +body: | + bb.1.entry: + successors: %bb.2.loop_body(0x80000000) + liveins: %w0 + ; Test that we properly legalize a phi that uses a value from the same BB + ; CHECK-LABEL: name: legalize_phi_loop + ; CHECK-LABEL: bb.0.entry: + ; CHECK: [[C0:%.*]](s32) = G_CONSTANT i32 0 + ; CHECK: [[RES_BB1:%.*]](s16) = G_TRUNC [[C0]] + + ; CHECK-LABEL: bb.1.loop_body: + ; CHECK: [[RES_PHI:%.*]](s16) = G_PHI [[RES_BB1]](s16), %bb.0.entry, [[RES_BB2:%.*]](s16), %bb.1.loop_body + ; CHECK: [[RES_BB2]](s16) = G_ANYEXT + %0(s32) = COPY %w0 + %2(s8) = G_CONSTANT i8 1 + %7(s8) = G_CONSTANT i8 0 + + bb.2.loop_body: + successors: %bb.2.loop_body(0x40000000), %bb.3.return(0x40000000) + + %1(s8) = G_PHI %7(s8), %bb.1.entry, %3(s8), %bb.2.loop_body + %3(s8) = G_ADD %1, %2 + %4(s32) = G_ZEXT %3(s8) + %5(s1) = G_ICMP intpred(ugt), %4(s32), %0 + G_BRCOND %5(s1), %bb.2.loop_body + + bb.3.return: + %6(s32) = G_ZEXT %3(s8) + %w0 = COPY %6(s32) + RET_ReallyLR implicit %w0 + +... +--- +name: legalize_phi_cycle +alignment: 2 +exposesReturnsTwice: false +legalized: false +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: '' } +liveins: +body: | + bb.1.entry: + successors: %bb.2.loop_body(0x80000000) + liveins: %w0 + ; Test that we properly legalize a phi that uses itself + ; CHECK-LABEL: name: legalize_phi_cycle + ; CHECK-LABEL: bb.0.entry: + ; CHECK: [[C0:%.*]](s32) = G_CONSTANT i32 0 + ; CHECK: [[RES_BB1:%.*]](s16) = G_TRUNC [[C0]] + + ; CHECK-LABEL: bb.1.loop_body: + ; CHECK: [[RES_PHI:%.*]](s16) = G_PHI [[RES_BB1]](s16), %bb.0.entry, [[RES_BB2:%.*]](s16), %bb.1.loop_body + ; CHECK: [[RES_BB2]](s16) = G_ANYEXT + + %0(s32) = COPY %w0 + %4(s8) = G_CONSTANT i8 0 + + bb.2.loop_body: + successors: %bb.2.loop_body(0x40000000), %bb.3.return(0x40000000) + + %1(s8) = G_PHI %4(s8), %bb.1.entry, %1(s8), %bb.2.loop_body + %2(s32) = G_ZEXT %1(s8) + %3(s1) = G_ICMP intpred(ugt), %2(s32), %0 + G_BRCOND %3(s1), %bb.2.loop_body + + bb.3.return: + %w0 = COPY %2(s32) + RET_ReallyLR implicit %w0 + +... +