diff --git a/llvm/lib/CodeGen/Spill2Reg.cpp b/llvm/lib/CodeGen/Spill2Reg.cpp --- a/llvm/lib/CodeGen/Spill2Reg.cpp +++ b/llvm/lib/CodeGen/Spill2Reg.cpp @@ -71,11 +71,22 @@ /// differs across accesses to the same stack slot. unsigned MemBits = 0; #ifndef NDEBUG - LLVM_DUMP_METHOD void dump() const; + LLVM_DUMP_METHOD virtual void dump() const; + virtual ~MIData() {} +#endif + }; + + struct MIDataWithLiveIn : public MIData { + MIDataWithLiveIn(MachineInstr *MI, const MachineOperand *MO, + unsigned MemBits) + : MIData(MI, MO, MemBits) {} + bool IsLiveIn = true; +#ifndef NDEBUG + LLVM_DUMP_METHOD virtual void dump() const override; #endif }; SmallVector Spills; - SmallVector Reloads; + SmallVector Reloads; /// \Returns the register class of the register spilled/reloaded. const TargetRegisterClass *getRegClass(MachineRegisterInfo *MRI) const { @@ -203,6 +214,14 @@ continue; } Entry.Spills.emplace_back(Spill, MO, MemBits); + + // If any of the reloads collected so far is in the same MBB then then + // mark it as non live-in. This is used during code generation when we + // update the liveins of MBBs to include the new vector register. + // Doing this now avoids an MBB walk which should save compilation time. + for (auto &MID : Entry.Reloads) + if (MID.MI->getParent() == &MBB) + MID.IsLiveIn = false; } else if (const MachineOperand *MO = TII->isLoadFromStackSlotMO(MI, StackSlot)) { MachineInstr *Reload = &MI; @@ -272,6 +291,47 @@ for (MachineBasicBlock *PredMBB : MBB->predecessors()) DFS(PredMBB, Visited, Fn); } + +void Spill2Reg::updateLiveIns(StackSlotDataEntry &Entry, MCRegister VectorReg) { + // Collect the parent MBBs of Spills for fast lookup. + DenseSet SpillMBBs(Entry.Spills.size()); + DenseSet Spills(Entry.Spills.size()); + for (const auto &Data : Entry.Spills) { + SpillMBBs.insert(Data.MI->getParent()); + Spills.insert(Data.MI); + } + + auto AddLiveInIfRequired = [VectorReg, &SpillMBBs](MachineBasicBlock *MBB) { + // If there is a spill in this MBB then we don't need to add a live-in. + // This works even if there is a reload above the spill, like this: + // reload stack.0 + // spill stack.0 + // because the live-in due to the reload is handled at a separate walk. + if (SpillMBBs.count(MBB)) + // Return true to stop the recursion. + return true; + // If there are no spills in this block then the register is live-in. + if (!MBB->isLiveIn(VectorReg)) + MBB->addLiveIn(VectorReg); + // Return false to continue the recursion. + return false; + }; + + // Update the MBB live-ins. These are used for the live regs calculation. + // Collect the parent MBBs of Spills for fast lookup. + DenseSet Visited; + for (const auto &ReloadData : Entry.Reloads) { + MachineInstr *Reload = ReloadData.MI; + MachineBasicBlock *MBB = Reload->getParent(); + if (ReloadData.IsLiveIn) { + if (!MBB->isLiveIn(VectorReg)) + MBB->addLiveIn(VectorReg); + } + for (MachineBasicBlock *PredMBB : Reload->getParent()->predecessors()) + DFS(PredMBB, Visited, AddLiveInIfRequired); + } +} + // Replace stack-based spills/reloads with register-based ones. void Spill2Reg::replaceStackWithReg(StackSlotDataEntry &Entry, Register VectorReg) { @@ -415,6 +475,12 @@ if (!PhysVectorRegOpt) continue; + // Update the MBB live-ins. These are used for the live regs calculation. + // Collect the parent MBBs of Spills for fast lookup. + // NOTE: We do that before calling replaceStackWithReg() because it will + // remove the spill/reload instructions from Entry. + updateLiveIns(Entry, *PhysVectorRegOpt); + // Replace stack accesses with register accesses. replaceStackWithReg(Entry, *PhysVectorRegOpt); @@ -444,6 +510,10 @@ dbgs() << " (" << *MO << ") " << *MI; } +void Spill2Reg::StackSlotDataEntry::MIDataWithLiveIn::dump() const { + dbgs() << " (" << *MO << ") " << *MI << " IsLiveIn: " << IsLiveIn; +} + void Spill2Reg::StackSlotDataEntry::dump() const { dbgs().indent(DumpInd) << "Disable: " << Disable << "\n"; dbgs().indent(DumpInd) << "Spills:\n"; diff --git a/llvm/test/CodeGen/X86/spill2reg_liveins_reload_before_spill.mir b/llvm/test/CodeGen/X86/spill2reg_liveins_reload_before_spill.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/spill2reg_liveins_reload_before_spill.mir @@ -0,0 +1,74 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc %s -o - -mtriple=x86_64-unknown-linux -enable-spill2reg -mattr=+sse4.1 --run-pass=spill2reg -simplify-mir -spill2reg-mem-instrs=0 -spill2reg-vec-instrs=99999 | FileCheck %s + +# Check that the liveins of a MBB get updated correctly if it contains a reload +# before a spill. + +# bb.0: +# spill stack.0 +# JMP bb.1 +# +# bb.1: +# reload stack.0 +# spill stack.0 +# JMP bb.2 +# +# bb.2: +# reload stack.0 + + +--- | + @D0 = dso_local local_unnamed_addr global i32 0, align 4 + @U0 = dso_local local_unnamed_addr global i32 0, align 4 + define void @func() { ret void } +... +--- +name: func +alignment: 16 +tracksRegLiveness: true +tracksDebugUserValues: true +frameInfo: + maxAlignment: 4 +stack: + - { id: 0, type: spill-slot, size: 4, alignment: 4 } +machineFunctionInfo: {} +body: | + ; CHECK-LABEL: name: func + ; CHECK: bb.0: + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + ; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax + ; CHECK-NEXT: JMP_1 %bb.1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: liveins: $xmm0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0 + ; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax + ; CHECK-NEXT: JMP_1 %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: liveins: $xmm0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0 + ; CHECK-NEXT: RET 0 + + + + bb.0: + $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + ; Spill stack.0 + MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0) + JMP_1 %bb.1 + + bb.1: + ; Reload stack.0 + $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) + ; Spill stack.0 + MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0) + JMP_1 %bb.2 + + bb.2: + ; Reload + $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) + RET 0 +... diff --git a/llvm/test/CodeGen/X86/spill2reg_liveins_spill_override.mir b/llvm/test/CodeGen/X86/spill2reg_liveins_spill_override.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/spill2reg_liveins_spill_override.mir @@ -0,0 +1,103 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc %s -o - -mtriple=x86_64-unknown-linux -enable-spill2reg -mattr=+sse4.1 --run-pass=spill2reg -simplify-mir -spill2reg-mem-instrs=0 -spill2reg-vec-instrs=99999 | FileCheck %s + + +# Checks that spills that are overriden by other spills (like spill0 by spill1) +# are not marked as live-in. +# Also checks live reg tracking with spills-reload pairs separated by an instr +# that clobbers the xmm registers. + +# BB0: +# [stack.0] = ... ; spill0 +# BB1: +# [stack.0] = ... ; spill1 +# ... = [stack.0] ; reload1 +# call ; clobbers xmm regs +# [stack.0] = ... ; spill2 +# ... = [stack.0] ; reload2 +# BB2: +# ... = [stack.0] ; reload3 + +--- | + @D0 = dso_local local_unnamed_addr global i32 0, align 4 + @U0 = dso_local local_unnamed_addr global i32 0, align 4 + declare void @foo() + define void @func() { ret void } +... +--- +name: func +alignment: 16 +tracksRegLiveness: true +tracksDebugUserValues: true +frameInfo: + maxAlignment: 4 +stack: + - { id: 0, type: spill-slot, size: 4, alignment: 4 } +machineFunctionInfo: {} +body: | + ; CHECK-LABEL: name: func + ; CHECK: bb.0: + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + ; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + ; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0 + ; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + ; CHECK-NEXT: CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit-def $rsp + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + ; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0 + ; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + ; CHECK-NEXT: JMP_1 %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: liveins: $xmm0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0 + ; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + ; CHECK-NEXT: RET 0 + + + + bb.0: + successors: %bb.1 + $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + ; spill0 + MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0) + + bb.1: + successors: %bb.2 + ; spill1 + $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0) + + ; reload1 + $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) + MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + + ; The call clobbers all xmm regs + CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit-def $rsp + + ; spill2 + $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0) + + ; reload2 + $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) + MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + JMP_1 %bb.2 + + bb.2: + ; reload3 + $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) + MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + RET 0 +... + + + + + + diff --git a/llvm/test/CodeGen/X86/spill2reg_liveregs_bbs.mir b/llvm/test/CodeGen/X86/spill2reg_liveregs_bbs.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/spill2reg_liveregs_bbs.mir @@ -0,0 +1,75 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc %s -o - -mtriple=x86_64-unknown-linux -enable-spill2reg -mattr=+sse4.1 --run-pass=spill2reg -simplify-mir -spill2reg-mem-instrs=0 -spill2reg-vec-instrs=99999 | FileCheck %s + +# Check that live reg tracking works correctly when one live range spans +# multiple blocks and the other spans a single block. +# BB0: +# [stack.0] = ... +# BB1: +# [stack.1] = ... +# ... = [stack.1] +# BB2: +# ... = [stack.0] + +--- | + @D0 = dso_local local_unnamed_addr global i32 0, align 4 + @D1 = dso_local local_unnamed_addr global i32 0, align 4 + @U0 = dso_local local_unnamed_addr global i32 0, align 4 + @U1 = dso_local local_unnamed_addr global i32 0, align 4 + define void @func() { ret void } +... +--- +name: func +alignment: 16 +tracksRegLiveness: true +tracksDebugUserValues: true +frameInfo: + maxAlignment: 4 +stack: + - { id: 0, type: spill-slot, size: 4, alignment: 4 } + - { id: 1, type: spill-slot, size: 4, alignment: 4 } +machineFunctionInfo: {} +body: | + ; CHECK-LABEL: name: func + ; CHECK: bb.0: + ; CHECK-NEXT: liveins: $xmm1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + ; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: liveins: $xmm0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D1, $noreg :: (dereferenceable load (s32) from @D1) + ; CHECK-NEXT: $xmm1 = MOVDI2PDIrr $eax + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm1 + ; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U1, $noreg, killed renamable $eax :: (store (s32) into @U1) + ; CHECK-NEXT: JMP_1 %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: liveins: $xmm0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0 + ; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + ; CHECK-NEXT: RET 0 + + + + bb.0: + successors: %bb.1 + $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0) + + bb.1: + successors: %bb.2 + $eax = MOV32rm $rip, 1, $noreg, @D1, $noreg :: (dereferenceable load (s32) from @D1) + MOV32mr %stack.1, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.1) + $eax = MOV32rm %stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %stack.1) + MOV32mr $rip, 1, $noreg, @U1, $noreg, killed renamable $eax :: (store (s32) into @U1) + JMP_1 %bb.2 + + bb.2: + $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) + MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + RET 0 +... diff --git a/llvm/test/CodeGen/X86/spill2reg_liveregs_cross.mir b/llvm/test/CodeGen/X86/spill2reg_liveregs_cross.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/spill2reg_liveregs_cross.mir @@ -0,0 +1,114 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc %s -o - -mtriple=x86_64-unknown-linux -enable-spill2reg -mattr=+sse4.1 --run-pass=spill2reg -simplify-mir -spill2reg-mem-instrs=0 -spill2reg-vec-instrs=99999 | FileCheck %s + +# Check that reg liveness works correctly when the live ranges cross like this: +# BB0 BB1 +# \ / +# BB2 +# / \ +# BB3 BB4 +# +# BB0: +# [stack.0] = ... +# JMP BB2 +# BB1: +# [stack.1] = ... +# JMP BB2 +# BB2: +# ... +# JMP BB3 or BB4 +# BB3: +# ... = [stack.0] +# BB4: +# ... = [stack.1] + +--- | + @D0 = dso_local local_unnamed_addr global i32 0, align 4 + @D1 = dso_local local_unnamed_addr global i32 0, align 4 + @U0 = dso_local local_unnamed_addr global i32 0, align 4 + @U1 = dso_local local_unnamed_addr global i32 0, align 4 + @Cond = dso_local local_unnamed_addr global i32 0, align 4 + define void @func() { ret void } +... +--- +name: func +alignment: 16 +tracksRegLiveness: true +tracksDebugUserValues: true +frameInfo: + maxAlignment: 4 +stack: + - { id: 0, type: spill-slot, size: 4, alignment: 4 } + - { id: 1, type: spill-slot, size: 4, alignment: 4 } +machineFunctionInfo: {} +body: | + ; CHECK-LABEL: name: func + ; CHECK: bb.0: + ; CHECK-NEXT: liveins: $xmm1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + ; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax + ; CHECK-NEXT: JMP_1 %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: liveins: $xmm0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D1, $noreg :: (dereferenceable load (s32) from @D1) + ; CHECK-NEXT: $xmm1 = MOVDI2PDIrr $eax + ; CHECK-NEXT: JMP_1 %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: liveins: $xmm0, $xmm1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @Cond, $noreg :: (dereferenceable load (s32) from @Cond) + ; CHECK-NEXT: TEST32rr $eax, $eax, implicit-def $eflags + ; CHECK-NEXT: JCC_1 %bb.3, 2, implicit $eflags + ; CHECK-NEXT: JMP_1 %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3: + ; CHECK-NEXT: liveins: $xmm0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0 + ; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + ; CHECK-NEXT: RET 0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4: + ; CHECK-NEXT: liveins: $xmm1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm1 + ; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U1, $noreg, killed renamable $eax :: (store (s32) into @U1) + ; CHECK-NEXT: RET 0 + + + + + + bb.0: + successors: %bb.2 + $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0) + JMP_1 %bb.2 + + bb.1: + successors: %bb.2 + $eax = MOV32rm $rip, 1, $noreg, @D1, $noreg :: (dereferenceable load (s32) from @D1) + MOV32mr %stack.1, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.1) + JMP_1 %bb.2 + + bb.2: + $eax = MOV32rm $rip, 1, $noreg, @Cond, $noreg :: (dereferenceable load (s32) from @Cond) + TEST32rr $eax, $eax, implicit-def $eflags + JCC_1 %bb.3, 2, implicit $eflags + JMP_1 %bb.4 + + bb.3: + $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) + MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + RET 0 + + bb.4: + $eax = MOV32rm %stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %stack.1) + MOV32mr $rip, 1, $noreg, @U1, $noreg, killed renamable $eax :: (store (s32) into @U1) + RET 0 + +... diff --git a/llvm/test/CodeGen/X86/spill2reg_liveregs_vec_under_reload.mir b/llvm/test/CodeGen/X86/spill2reg_liveregs_vec_under_reload.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/spill2reg_liveregs_vec_under_reload.mir @@ -0,0 +1,90 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc %s -o - -mtriple=x86_64-unknown-linux -enable-spill2reg -mattr=+sse4.1 --run-pass=spill2reg -simplify-mir -spill2reg-mem-instrs=0 -spill2reg-vec-instrs=99999 | FileCheck %s + +# Check that reg liveness does not get confused by vector reg accesses under +# a reload. +# +# BB0 +# / \ +# BB1 BB2 +# +# BB0: +# [stack.0] = ... +# xmm0 = ... +# ... = xmm0 +# JMP BB1 or BB2 +# BB1: +# ... = [stack.0] +# BB2: +# ... = [stack.0] +# xmm0 = ... + + +--- | + @D0 = dso_local local_unnamed_addr global i32 0, align 4 + @U0 = dso_local local_unnamed_addr global i32 0, align 4 + @Cond = dso_local local_unnamed_addr global i32 0, align 4 + define void @func() { ret void } +... +--- +name: func +tracksRegLiveness: true +stack: + - { id: 0, type: spill-slot, size: 4, alignment: 4 } +machineFunctionInfo: {} +body: | + ; CHECK-LABEL: name: func + ; CHECK: bb.0: + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + ; CHECK-NEXT: $xmm1 = MOVDI2PDIrr $eax + ; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @Cond, $noreg :: (dereferenceable load (s32) from @Cond) + ; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0 + ; CHECK-NEXT: TEST32rr $eax, $eax, implicit-def $eflags + ; CHECK-NEXT: JCC_1 %bb.1, 2, implicit $eflags + ; CHECK-NEXT: JMP_1 %bb.2 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1: + ; CHECK-NEXT: liveins: $xmm1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm1 + ; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + ; CHECK-NEXT: RET 0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2: + ; CHECK-NEXT: liveins: $xmm1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm1 + ; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, $eax :: (store (s32) into @U0) + ; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax + ; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0 + ; CHECK-NEXT: RET 0 + + + + bb.0: + successors: %bb.1, %bb.2 + $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0) + MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0) + $eax = MOV32rm $rip, 1, $noreg, @Cond, $noreg :: (dereferenceable load (s32) from @Cond) + ; xmm0 used here + $xmm0 = MOVDI2PDIrr $eax + $eax = MOVPDI2DIrr $xmm0 + + TEST32rr $eax, $eax, implicit-def $eflags + JCC_1 %bb.1, 2, implicit $eflags + JMP_1 %bb.2 + + bb.1: + $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) + MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0) + RET 0 + + bb.2: + $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) + MOV32mr $rip, 1, $noreg, @U0, $noreg, $eax :: (store (s32) into @U0) + ; xmm0 used here too, under the reload + $xmm0 = MOVDI2PDIrr $eax + $eax = MOVPDI2DIrr $xmm0 + RET 0 +...