Teach two-address lowering how to unfold a load to open up commuting
authorDan Gohman <gohman@apple.com>
Mon, 21 Jun 2010 22:17:20 +0000 (22:17 +0000)
committerDan Gohman <gohman@apple.com>
Mon, 21 Jun 2010 22:17:20 +0000 (22:17 +0000)
opportunities. For example, this lets it emit this:

   movq (%rax), %rcx
   addq %rdx, %rcx

instead of this:

   movq %rdx, %rcx
   addq (%rax), %rcx

in the case where %rdx has subsequent uses. It's the same number
of instructions, and usually the same encoding size on x86, but
it appears faster, and in general, it may allow better scheduling
for the load.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@106493 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/TwoAddressInstructionPass.cpp
test/CodeGen/X86/2007-01-08-InstrSched.ll
test/CodeGen/X86/lsr-reuse.ll
test/CodeGen/X86/pic.ll
test/CodeGen/X86/stack-align.ll
test/CodeGen/X86/tailcallstack64.ll

index 14102d1f3dd1708f4572c084adf0941ca143d384..6144ecd002e3d88b11e016786c6e0971ea5f8225 100644 (file)
@@ -898,6 +898,90 @@ TryInstructionTransform(MachineBasicBlock::iterator &mi,
       }
     }
   }
+
+  // If this is an instruction with a load folded into it, try unfolding
+  // the load, e.g. avoid this:
+  //   movq %rdx, %rcx
+  //   addq (%rax), %rcx
+  // in favor of this:
+  //   movq (%rax), %rcx
+  //   addq %rdx, %rcx
+  // because it's preferable to schedule a load than a register copy.
+  if (TID.mayLoad() && !regBKilled) {
+    // Determine if a load can be unfolded.
+    unsigned LoadRegIndex;
+    unsigned NewOpc =
+      TII->getOpcodeAfterMemoryUnfold(mi->getOpcode(),
+                                      /*UnfoldLoad=*/true,
+                                      /*UnfoldStore=*/false,
+                                      &LoadRegIndex);
+    if (NewOpc != 0) {
+      const TargetInstrDesc &UnfoldTID = TII->get(NewOpc);
+      if (UnfoldTID.getNumDefs() == 1) {
+        MachineFunction &MF = *mbbi->getParent();
+
+        // Unfold the load.
+        DEBUG(dbgs() << "2addr:   UNFOLDING: " << *mi);
+        const TargetRegisterClass *RC =
+          UnfoldTID.OpInfo[LoadRegIndex].getRegClass(TRI);
+        unsigned Reg = MRI->createVirtualRegister(RC);
+        SmallVector<MachineInstr *, 2> NewMIs;
+        bool Success =
+          TII->unfoldMemoryOperand(MF, mi, Reg,
+                                   /*UnfoldLoad=*/true, /*UnfoldStore=*/false,
+                                   NewMIs);
+        (void)Success;
+        assert(Success &&
+               "unfoldMemoryOperand failed when getOpcodeAfterMemoryUnfold "
+               "succeeded!");
+        assert(NewMIs.size() == 2 &&
+               "Unfolded a load into multiple instructions!");
+        // The load was previously folded, so this is the only use.
+        NewMIs[1]->addRegisterKilled(Reg, TRI);
+
+        // Tentatively insert the instructions into the block so that they
+        // look "normal" to the transformation logic.
+        mbbi->insert(mi, NewMIs[0]);
+        mbbi->insert(mi, NewMIs[1]);
+
+        DEBUG(dbgs() << "2addr:    NEW LOAD: " << *NewMIs[0]
+                     << "2addr:    NEW INST: " << *NewMIs[1]);
+
+        // Transform the instruction, now that it no longer has a load.
+        unsigned NewDstIdx = NewMIs[1]->findRegisterDefOperandIdx(regA);
+        unsigned NewSrcIdx = NewMIs[1]->findRegisterUseOperandIdx(regB);
+        MachineBasicBlock::iterator NewMI = NewMIs[1];
+        bool TransformSuccess =
+          TryInstructionTransform(NewMI, mi, mbbi,
+                                  NewSrcIdx, NewDstIdx, Dist);
+        if (TransformSuccess ||
+            NewMIs[1]->getOperand(NewSrcIdx).isKill()) {
+          // Success, or at least we made an improvement. Keep the unfolded
+          // instructions and discard the original.
+          if (LV) {
+            for (unsigned i = 0, e = mi->getNumOperands(); i != e; ++i) {
+              MachineOperand &MO = mi->getOperand(i);
+              if (MO.isReg() && MO.isUse() && MO.isKill())
+                LV->replaceKillInstruction(Reg, mi, NewMIs[0]);
+            }
+            LV->addVirtualRegisterKilled(Reg, NewMIs[1]);
+          }
+          mi->eraseFromParent();
+          mi = NewMIs[1];
+          if (TransformSuccess)
+            return true;
+        } else {
+          // Transforming didn't eliminate the tie and didn't lead to an
+          // improvement. Clean up the unfolded instructions and keep the
+          // original.
+          DEBUG(dbgs() << "2addr: ABANDONING UNFOLD\n");
+          NewMIs[0]->eraseFromParent();
+          NewMIs[1]->eraseFromParent();
+        }
+      }
+    }
+  }
+
   return false;
 }
 
index ef19d72150a0d806f727666e96d3a89d3e8be655..6f8b89c3240ddf19005eae8bbba1300068ddf7c9 100644 (file)
@@ -11,12 +11,12 @@ define float @foo(float %x) nounwind {
     %tmp14 = fadd float %tmp12, %tmp7
     ret float %tmp14
 
-; CHECK: mulss LCPI0_0(%rip)
-; CHECK: mulss LCPI0_1(%rip)
+; CHECK: mulss
+; CHECK: mulss
 ; CHECK: addss
-; CHECK: mulss LCPI0_2(%rip)
+; CHECK: mulss
 ; CHECK: addss
-; CHECK: mulss LCPI0_3(%rip)
+; CHECK: mulss
 ; CHECK: addss
 ; CHECK: ret
 }
index 3f4f9ec1d13287c36f51968164a7bef5f84366e7..b7e69b84bf845481dc93353e6bfe3efcb13745a8 100644 (file)
@@ -465,14 +465,14 @@ bb5:                                              ; preds = %bb3, %entry
 ; And the one at %bb68, where we want to be sure to use superhero mode:
 
 ; CHECK:      BB10_10:
-; CHECK-NEXT:   movaps  %xmm{{.*}}, %xmm{{.*}}
-; CHECK-NEXT:   mulps   48(%r{{[^,]*}}), %xmm{{.*}}
-; CHECK-NEXT:   movaps  %xmm{{.*}}, %xmm{{.*}}
-; CHECK-NEXT:   mulps   32(%r{{[^,]*}}), %xmm{{.*}}
-; CHECK-NEXT:   movaps  %xmm{{.*}}, %xmm{{.*}}
-; CHECK-NEXT:   mulps   16(%r{{[^,]*}}), %xmm{{.*}}
-; CHECK-NEXT:   movaps  %xmm{{.*}}, %xmm{{.*}}
-; CHECK-NEXT:   mulps   (%r{{[^,]*}}), %xmm{{.*}}
+; CHECK-NEXT:   movaps  48(%r{{[^,]*}}), %xmm{{.*}}
+; CHECK-NEXT:   mulps   %xmm{{.*}}, %xmm{{.*}}
+; CHECK-NEXT:   movaps  32(%r{{[^,]*}}), %xmm{{.*}}
+; CHECK-NEXT:   mulps   %xmm{{.*}}, %xmm{{.*}}
+; CHECK-NEXT:   movaps  16(%r{{[^,]*}}), %xmm{{.*}}
+; CHECK-NEXT:   mulps   %xmm{{.*}}, %xmm{{.*}}
+; CHECK-NEXT:   movaps  (%r{{[^,]*}}), %xmm{{.*}}
+; CHECK-NEXT:   mulps   %xmm{{.*}}, %xmm{{.*}}
 ; CHECK-NEXT:   movaps  %xmm{{.*}}, (%r{{[^,]*}})
 ; CHECK-NEXT:   movaps  %xmm{{.*}}, 16(%r{{[^,]*}})
 ; CHECK-NEXT:   movaps  %xmm{{.*}}, 32(%r{{[^,]*}})
index 9506c9b5db11217d01498750cffa9695216ffd81..d7b080525314dc313b3ad5eed1d3457b3a62ebfe 100644 (file)
@@ -189,7 +189,7 @@ bb12:
 ; LINUX:   call        .L7$pb
 ; LINUX: .L7$pb:
 ; LINUX:   addl        $_GLOBAL_OFFSET_TABLE_+(.L{{.*}}-.L7$pb),
-; LINUX:   addl        .LJTI7_0@GOTOFF(
+; LINUX:   .LJTI7_0@GOTOFF(
 ; LINUX:   jmpl        *
 
 ; LINUX: .LJTI7_0:
index 271ad1aad0ba91e616115569de5fe6bc0682e374..8ca0b12b547f8041a5c06ca2662a34bec28f8ca4 100644 (file)
@@ -9,14 +9,15 @@ target triple = "i686-apple-darwin8"
 
 define void @test({ double, double }* byval  %z, double* %P) {
 entry:
+       %tmp3 = load double* @G, align 16               ; <double> [#uses=1]
+       %tmp4 = tail call double @fabs( double %tmp3 )          ; <double> [#uses=1]
+        volatile store double %tmp4, double* %P
        %tmp = getelementptr { double, double }* %z, i32 0, i32 0               ; <double*> [#uses=1]
-       %tmp1 = load double* %tmp, align 8              ; <double> [#uses=1]
+       %tmp1 = volatile load double* %tmp, align 8             ; <double> [#uses=1]
        %tmp2 = tail call double @fabs( double %tmp1 )          ; <double> [#uses=1]
     ; CHECK: andpd{{.*}}4(%esp), %xmm
-       %tmp3 = load double* @G, align 16               ; <double> [#uses=1]
-       %tmp4 = tail call double @fabs( double %tmp3 )          ; <double> [#uses=1]
        %tmp6 = fadd double %tmp4, %tmp2                ; <double> [#uses=1]
-       store double %tmp6, double* %P, align 8
+       volatile store double %tmp6, double* %P, align 8
        ret void
 }
 
index d05dff8928bac29d2213133ea292a431f2507876..107bdf9de3e7af18ca1040406f6eb490f9526ced 100644 (file)
@@ -2,9 +2,11 @@
 
 ; Check that lowered arguments on the stack do not overwrite each other.
 ; Add %in1 %p1 to a different temporary register (%eax).
-; CHECK: movl  %edi, %eax
+; CHECK: movl  32(%rsp), %eax
 ; Move param %in1 to temp register (%r10d).
 ; CHECK: movl  40(%rsp), %r10d
+; Add %in1 %p1 to a different temporary register (%eax).
+; CHECK: addl %edi, %eax
 ; Move param %in2 to stack.
 ; CHECK: movl  %r10d, 32(%rsp)
 ; Move result of addition to stack.