Index: include/llvm/CodeGen/MachineRegisterInfo.h =================================================================== --- include/llvm/CodeGen/MachineRegisterInfo.h +++ include/llvm/CodeGen/MachineRegisterInfo.h @@ -112,6 +112,12 @@ return PhysRegUseDefLists[RegNo]; } + /// Helper method for constrainRegAttrs and a public overloaded version of + /// constrainRegClass. + inline const TargetRegisterClass * + constrainRegClass(unsigned Reg, const TargetRegisterClass *OldRC, + const TargetRegisterClass *RC, unsigned MinNumRegs); + /// Get the next element in the use-def chain. static MachineOperand *getNextOperandForReg(const MachineOperand *MO) { assert(MO && MO->isReg() && "This is not a register operand!"); @@ -657,6 +663,16 @@ const TargetRegisterClass *RC, unsigned MinNumRegs = 0); + /// constrainRegAttrs - Constrain the register class or the register bank of + /// the virtual register \p Reg to be a common subclass and a common bank + /// of both registers provided respectively. Do nothing if any of the + /// attributes (classes, banks, or low-level types) of the registers are + /// deemed incompatible, or if the resulting register will have a class + /// smaller than before and of size less than \p MinNumRegs. Return true if + /// such register attributes exist, false otherwise. + bool constrainRegAttrs(unsigned Reg, unsigned ConstrainingReg, + unsigned MinNumRegs = 0); + /// recomputeRegClass - Try to find a legal super-class of Reg's register /// class that still satisfies the constraints from the instructions using /// Reg. Returns true if Reg was upgraded. Index: lib/CodeGen/MachineCSE.cpp =================================================================== --- lib/CodeGen/MachineCSE.cpp +++ lib/CodeGen/MachineCSE.cpp @@ -176,8 +176,7 @@ // class given a super-reg class and subreg index. if (DefMI->getOperand(1).getSubReg()) continue; - const TargetRegisterClass *RC = MRI->getRegClass(Reg); - if (!MRI->constrainRegClass(SrcReg, RC)) + if (!MRI->constrainRegAttrs(SrcReg, Reg)) continue; DEBUG(dbgs() << "Coalescing: " << *DefMI); DEBUG(dbgs() << "*** to: " << *MI); @@ -588,11 +587,11 @@ break; } - // Don't perform CSE if the result of the old instruction cannot exist - // within the register class of the new instruction. - const TargetRegisterClass *OldRC = MRI->getRegClass(OldReg); - if (!MRI->constrainRegClass(NewReg, OldRC)) { - DEBUG(dbgs() << "*** Not the same register class, avoid CSE!\n"); + // Don't perform CSE if the result of the new instruction cannot exist + // within the constraints (register class, bank, or low-level type) of + // the old instruction. + if (!MRI->constrainRegAttrs(NewReg, OldReg)) { + DEBUG(dbgs() << "*** Not the same register constraints, avoid CSE!\n"); DoCSE = false; break; } Index: lib/CodeGen/MachineRegisterInfo.cpp =================================================================== --- lib/CodeGen/MachineRegisterInfo.cpp +++ lib/CodeGen/MachineRegisterInfo.cpp @@ -65,11 +65,11 @@ VRegInfo[Reg].first = &RegBank; } -const TargetRegisterClass * +inline const TargetRegisterClass * MachineRegisterInfo::constrainRegClass(unsigned Reg, - const TargetRegisterClass *RC, - unsigned MinNumRegs) { - const TargetRegisterClass *OldRC = getRegClass(Reg); + const TargetRegisterClass *OldRC, + const TargetRegisterClass *RC, + unsigned MinNumRegs) { if (OldRC == RC) return RC; const TargetRegisterClass *NewRC = @@ -82,6 +82,51 @@ return NewRC; } +const TargetRegisterClass * +MachineRegisterInfo::constrainRegClass(unsigned Reg, + const TargetRegisterClass *RC, + unsigned MinNumRegs) { + return constrainRegClass(Reg, getRegClass(Reg), RC, MinNumRegs); +} + +bool +MachineRegisterInfo::constrainRegAttrs(unsigned Reg, + unsigned ConstrainingReg, + unsigned MinNumRegs) { + auto const *OldRC = getRegClassOrNull(Reg); + auto const *RC = getRegClassOrNull(ConstrainingReg); + // A virtual register at any point must have either a low-level type + // or a class assigned, but not both. The only exception is the internals of + // GlobalISel's instruction selection pass, which is allowed to temporarily + // introduce registers with types and classes both. For that (and only for + // that) use RegisterBankInfo::constrainGenericRegister instead. + assert((OldRC || getType(Reg).isValid()) && "Reg has neither class nor type"); + assert((!OldRC || !getType(Reg).isValid()) && "Reg has class and type both"); + assert((RC || getType(ConstrainingReg).isValid()) && + "ConstrainingReg has neither class nor type"); + assert((!RC || !getType(ConstrainingReg).isValid()) && + "ConstrainingReg has class and type both"); + if (OldRC && RC) + return constrainRegClass(Reg, OldRC, RC, MinNumRegs); + // If one of the virtual registers is generic (used in generic machine + // instructions, has a low-level type, doesn't have a class), and the other is + // concrete (used in target specific instructions, doesn't have a low-level + // type, has a class), we can not unify them. + if (OldRC || RC) + return false; + // At this point, both registers are guaranteed to have a valid low-level + // type, and they must agree. + if (getType(Reg) != getType(ConstrainingReg)) + return false; + auto const *OldRB = getRegBankOrNull(Reg); + auto const *RB = getRegBankOrNull(ConstrainingReg); + if (OldRB) + return !RB || RB == OldRB; + if (RB) + setRegBank(Reg, *RB); + return true; +} + bool MachineRegisterInfo::recomputeRegClass(unsigned Reg) { const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); Index: test/CodeGen/AArch64/GlobalISel/machine-cse-mid-pipeline.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/machine-cse-mid-pipeline.mir @@ -0,0 +1,269 @@ +# RUN: llc -run-pass machine-cse -global-isel -verify-machineinstrs -mtriple aarch64-apple-ios %s -o - | FileCheck %s +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-apple-ios" + + define i32 @irtranslated() { + entry: + %a = add i32 1, 1 + %b = add i32 1, 1 + %r = add i32 %a, %b + ret i32 %r + } + + define i32 @regbankselected() { ret i32 0 } + define i32 @legalized() { ret i32 0 } + define i32 @legalized_sym() { ret i32 0 } + + define i64 @int_extensions() { + entry: + %a1 = sext i8 1 to i16 + %b1 = sext i8 1 to i32 + %a2 = zext i16 %a1 to i64 + %b2 = zext i32 %b1 to i64 + %r = add i64 %a2, %b2 + ret i64 %r + } + + define i32 @generic(i32 %a, i32 %b) { + entry: + %sg = add i32 %a, %b + %ds = add i32 %sg, %sg + ret i32 %ds + } + + define i32 @generic_to_concrete_copy(i32 %a, i32 %b) { ret i32 0 } + define i32 @concrete_to_generic_copy(i32 %a, i32 %b) { ret i32 0 } + define i32 @concrete(i32 %a, i32 %b) { ret i32 0 } +... +--- +name: irtranslated +legalized: false +regBankSelected: false +selected: false +registers: + - { id: 0, class: _, preferred-register: '' } + - { id: 1, class: _, preferred-register: '' } + - { id: 2, class: _, preferred-register: '' } + - { id: 3, class: _, preferred-register: '' } +body: | + ; CHECK-LABEL: name: irtranslated + ; CHECK: %[[ONE:[0-9]+]]:_(s32) = G_CONSTANT i32 1 + ; CHECK-NEXT: %[[TWO:[0-9]+]]:_(s32) = G_ADD %[[ONE]], %[[ONE]] + ; CHECK-NEXT: %[[SUM:[0-9]+]]:_(s32) = G_ADD %[[TWO]], %[[TWO]] + ; CHECK-NEXT: %[[RET:[wx][0-9]+]] = COPY %[[SUM]](s32) + ; CHECK-NEXT: RET_ReallyLR implicit %[[RET]] + bb.0: + %0:_(s32) = G_CONSTANT i32 1 + %1:_(s32) = G_ADD %0, %0 + %2:_(s32) = G_ADD %0, %0 + %3:_(s32) = G_ADD %1, %2 + %w0 = COPY %3(s32) + RET_ReallyLR implicit %w0 +... +--- +name: regbankselected +legalized: true +regBankSelected: true +selected: false +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: '' } +body: | + ; CHECK-LABEL: name: regbankselected + ; CHECK: %[[ONE:[0-9]+]]:gpr(s32) = G_CONSTANT i32 1 + ; CHECK-NEXT: %[[TWO:[0-9]+]]:gpr(s32) = G_ADD %[[ONE]], %[[ONE]] + ; CHECK-NEXT: %[[SUM:[0-9]+]]:gpr(s32) = G_ADD %[[TWO]], %[[TWO]] + ; CHECK-NEXT: %[[RET:[wx][0-9]+]] = COPY %[[SUM]](s32) + ; CHECK-NEXT: RET_ReallyLR implicit %[[RET]] + bb.0: + %0:gpr(s32) = G_CONSTANT i32 1 + %1:gpr(s32) = G_ADD %0, %0 + %2:gpr(s32) = G_ADD %0, %0 + %3:gpr(s32) = G_ADD %1, %2 + %w0 = COPY %3(s32) + RET_ReallyLR implicit %w0 +... +--- +name: legalized +legalized: true +regBankSelected: false +selected: false +registers: + - { id: 0, class: _, preferred-register: '' } + - { id: 1, class: _, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } + - { id: 3, class: _, preferred-register: '' } +body: | + ; CHECK-LABEL: name: legalized + ; CHECK: %[[ONE:[0-9]+]]:_(s32) = G_CONSTANT i32 1 + ; CHECK-NEXT: %[[TWO:[0-9]+]]:gpr(s32) = G_ADD %[[ONE]], %[[ONE]] + ; CHECK-NEXT: %[[SUM:[0-9]+]]:_(s32) = G_ADD %[[TWO]], %[[TWO]] + ; CHECK-NEXT: %[[RET:[wx][0-9]+]] = COPY %[[SUM]](s32) + ; CHECK-NEXT: RET_ReallyLR implicit %[[RET]] + bb.0: + %0:_(s32) = G_CONSTANT i32 1 + %1:_(s32) = G_ADD %0, %0 + %2:gpr(s32) = G_ADD %0, %0 + %3:_(s32) = G_ADD %1, %2 + %w0 = COPY %3(s32) + RET_ReallyLR implicit %w0 +... +--- +name: legalized_sym +legalized: true +regBankSelected: false +selected: false +registers: + - { id: 0, class: _, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: _, preferred-register: '' } + - { id: 3, class: _, preferred-register: '' } +body: | + ; CHECK-LABEL: name: legalized_sym + ; CHECK: %[[ONE:[0-9]+]]:_(s32) = G_CONSTANT i32 1 + ; CHECK-NEXT: %[[TWO:[0-9]+]]:gpr(s32) = G_ADD %[[ONE]], %[[ONE]] + ; CHECK-NEXT: %[[SUM:[0-9]+]]:_(s32) = G_ADD %[[TWO]], %[[TWO]] + ; CHECK-NEXT: %[[RET:[wx][0-9]+]] = COPY %[[SUM]](s32) + ; CHECK-NEXT: RET_ReallyLR implicit %[[RET]] + bb.0: + %0:_(s32) = G_CONSTANT i32 1 + %1:gpr(s32) = G_ADD %0, %0 + %2:_(s32) = G_ADD %0, %0 + %3:_(s32) = G_ADD %1, %2 + %w0 = COPY %3(s32) + RET_ReallyLR implicit %w0 +... +--- +name: int_extensions +alignment: 2 +legalized: false +regBankSelected: false +selected: false +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: '' } +body: | + ; CHECK-LABEL: name: int_extensions + ; CHECK: %[[ONE:[0-9]+]]:_(s8) = G_CONSTANT i8 1 + ; CHECK-NEXT: %[[S16:[0-9]+]]:_(s16) = G_SEXT %[[ONE]](s8) + ; CHECK-NEXT: %[[S32:[0-9]+]]:_(s32) = G_SEXT %[[ONE]](s8) + ; CHECK-NEXT: %[[S16_Z64:[0-9]+]]:_(s64) = G_ZEXT %[[S16]](s16) + ; CHECK-NEXT: %[[S32_Z64:[0-9]+]]:_(s64) = G_ZEXT %[[S32]](s32) + ; CHECK-NEXT: %[[SUM:[0-9]+]]:_(s64) = G_ADD %[[S16_Z64]], %[[S32_Z64]] + ; CHECK-NEXT: %[[RET:[wx][0-9]+]] = COPY %[[SUM]](s64) + ; CHECK-NEXT: RET_ReallyLR implicit %[[RET]] + bb.0.entry: + %0:_(s8) = G_CONSTANT i8 1 + %1:_(s16) = G_SEXT %0(s8) + %2:_(s32) = G_SEXT %0(s8) + %3:_(s64) = G_ZEXT %1(s16) + %4:_(s64) = G_ZEXT %2(s32) + %5:_(s64) = G_ADD %3, %4 + %x0 = COPY %5(s64) + RET_ReallyLR implicit %x0 +... +--- +name: generic +legalized: true +regBankSelected: false +selected: false +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: '' } +body: | + ; CHECK-LABEL: name: generic + ; CHECK: %[[SG:[0-9]+]]:_(s32) = G_ADD %{{[0-9]+}}, %{{[0-9]+}} + ; CHECK-NEXT: %{{[0-9]+}}:_(s32) = G_ADD %[[SG]], %[[SG]] + bb.0: + %0:_(s32) = COPY %w0 + %1:_(s32) = COPY %w1 + %2:_(s32) = G_ADD %0, %1 + %3:_(s32) = COPY %2(s32) + %4:_(s32) = G_ADD %3, %3 + %w0 = COPY %4(s32) + RET_ReallyLR implicit %w0 +... +--- +name: generic_to_concrete_copy +legalized: true +regBankSelected: false +selected: false +registers: + - { id: 0, class: _, preferred-register: '' } + - { id: 1, class: _, preferred-register: '' } + - { id: 2, class: _, preferred-register: '' } + - { id: 3, class: gpr32, preferred-register: '' } + - { id: 4, class: gpr32, preferred-register: '' } +body: | + ; CHECK-LABEL: name: generic_to_concrete_copy + ; CHECK: %[[S1:[0-9]+]]:_(s32) = G_ADD %{{[0-9]+}}, %{{[0-9]+}} + ; CHECK-NEXT: %[[S2:[0-9]+]]:gpr32 = COPY %[[S1]](s32) + ; CHECK-NEXT: %{{[0-9]+}}:gpr32 = ADDWrr %[[S2]], %[[S2]] + bb.0: + %0:_(s32) = COPY %w0 + %1:_(s32) = COPY %w1 + %2:_(s32) = G_ADD %0, %1 + %3:gpr32 = COPY %2(s32) + %4:gpr32 = ADDWrr %3, %3 + %w0 = COPY %4 + RET_ReallyLR implicit %w0 +... +--- +name: concrete_to_generic_copy +legalized: true +regBankSelected: false +selected: false +registers: + - { id: 0, class: gpr32, preferred-register: '' } + - { id: 1, class: gpr32, preferred-register: '' } + - { id: 2, class: gpr32, preferred-register: '' } + - { id: 3, class: _, preferred-register: '' } + - { id: 4, class: _, preferred-register: '' } +body: | + ; CHECK-LABEL: name: concrete_to_generic_copy + ; CHECK: %[[S1:[0-9]+]]:gpr32 = ADDWrr %{{[0-9]+}}, %{{[0-9]+}} + ; CHECK-NEXT: %[[S2:[0-9]+]]:_(s32) = COPY %[[S1]] + ; CHECK-NEXT: %{{[0-9]+}}:_(s32) = G_ADD %[[S2]], %[[S2]] + bb.0: + %0:gpr32 = COPY %w0 + %1:gpr32 = COPY %w1 + %2:gpr32 = ADDWrr %0, %1 + %3:_(s32) = COPY %2 + %4:_(s32) = G_ADD %3, %3 + %w0 = COPY %4(s32) + RET_ReallyLR implicit %w0 +... +--- +name: concrete +legalized: true +regBankSelected: false +selected: false +registers: + - { id: 0, class: gpr32, preferred-register: '' } + - { id: 1, class: gpr32, preferred-register: '' } + - { id: 2, class: gpr32, preferred-register: '' } + - { id: 3, class: gpr32, preferred-register: '' } + - { id: 4, class: gpr32, preferred-register: '' } +body: | + ; CHECK-LABEL: name: concrete + ; CHECK: %[[SC:[0-9]+]]:gpr32 = ADDWrr %{{[0-9]+}}, %{{[0-9]+}} + ; CHECK-NEXT: %{{[0-9]+}}:gpr32 = ADDWrr %[[SC]], %[[SC]] + bb.0: + %0:gpr32 = COPY %w0 + %1:gpr32 = COPY %w1 + %2:gpr32 = ADDWrr %0, %1 + %3:gpr32 = COPY %2 + %4:gpr32 = ADDWrr %3, %3 + %w0 = COPY %4 + RET_ReallyLR implicit %w0 +...