Implement more complete and correct codegen for bitfield inserts, as tested
authorNate Begeman <natebegeman@mac.com>
Sun, 24 Oct 2004 10:33:30 +0000 (10:33 +0000)
committerNate Begeman <natebegeman@mac.com>
Sun, 24 Oct 2004 10:33:30 +0000 (10:33 +0000)
by the recently committed rlwimi.ll test file.  Also commit initial code
for bitfield extract, although it is turned off until fully debugged.

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

lib/Target/PowerPC/PPC32ISelSimple.cpp

index f5a21f07c06faa3c35df99b9facf518ca38b6976..d480d737e15ccb53688957f753fc33d928a85e92 100644 (file)
@@ -32,8 +32,6 @@
 using namespace llvm;
 
 namespace {
-  Statistic<> Bitfields("ppc-codegen", "Number of bitfield inserts");
-
   /// TypeClass - Used by the PowerPC backend to group LLVM types by their basic
   /// PPC Representation.
   ///
@@ -99,19 +97,18 @@ namespace {
       FoldedGEP(unsigned b, unsigned i, ConstantSInt *o) : 
         base(b), index(i), offset(o) {}
     };
-
-    /// RlwimiRec - This struct is for recording the necessary information to
-    /// emit a PowerPC rlwimi instruction for a bitfield insert rather than
-    /// a sequence of shifts and ands, followed by an or.
-    struct RlwimiRec {
-      unsigned Shift;
-      unsigned MB, ME;
-      Value *Op0, *Op1;
-      RlwimiRec() : Shift(0), MB(0), ME(0), Op0(0), Op1(0) {}
-      RlwimiRec(unsigned s, unsigned b, unsigned e, Value *y, Value *z) :
-        Shift(s), MB(b), ME(e), Op0(y), Op1(z) {}
+    
+    /// RlwimiRec - This struct is for recording the arguments to a PowerPC 
+    /// rlwimi instruction to be output for a particular Instruction::Or when
+    /// we recognize the pattern for rlwimi, starting with a shift or and.
+    struct RlwimiRec { 
+      Value *Target, *Insert;
+      unsigned Shift, MB, ME;
+      RlwimiRec() : Target(0), Insert(0), Shift(0), MB(0), ME(0) {}
+      RlwimiRec(Value *tgt, Value *ins, unsigned s, unsigned b, unsigned e) :
+        Target(tgt), Insert(ins), Shift(s), MB(b), ME(e) {}
     };
-
+    
     // External functions used in the Module
     Function *fmodfFn, *fmodFn, *__cmpdi2Fn, *__moddi3Fn, *__divdi3Fn, 
       *__umoddi3Fn,  *__udivdi3Fn, *__fixsfdiFn, *__fixdfdiFn, *__fixunssfdiFn,
@@ -132,7 +129,11 @@ namespace {
     
     // RlwimiMap  - Mapping between BinaryOperand (Or) instructions and info
     // needed to properly emit a rlwimi instruction in its place.
-    std::map<BinaryOperator *, RlwimiRec> RlwimiMap;
+    std::map<Instruction *, RlwimiRec> InsertMap;
+
+    // A rlwimi instruction is the combination of at least three instructions.
+    // Keep a vector of instructions to skip around so that we do not try to
+    // emit instructions that were folded into a rlwimi.
     std::vector<Instruction *> SkipList;
 
     // A Reg to hold the base address used for global loads and stores, and a
@@ -215,8 +216,8 @@ namespace {
       GEPMap.clear();
       RegMap.clear();
       MBBMap.clear();
+      InsertMap.clear();
       AllocaMap.clear();
-      RlwimiMap.clear();
       SkipList.clear();
       F = 0;
       // We always build a machine code representation for the function
@@ -339,10 +340,18 @@ namespace {
 
 
     /// emitBitfieldInsert - return true if we were able to fold the sequence of
-    /// instructions starting with AndI into a bitfield insert.
-    bool PPC32ISel::emitBitfieldInsert(BinaryOperator *AndI, 
-                                       unsigned ShlAmount,
-                                       Value *InsertOp);
+    /// instructions into a bitfield insert (rlwimi).
+    bool emitBitfieldInsert(MachineBasicBlock *MBB, 
+                            MachineBasicBlock::iterator IP,
+                            User *OpUser, unsigned DestReg);
+                                  
+    /// emitBitfieldExtract - return true if we were able to fold the sequence
+    /// of instructions into a bitfield extract (rlwinm).
+    bool emitBitfieldExtract(MachineBasicBlock *MBB, 
+                             MachineBasicBlock::iterator IP,
+                             BinaryOperator *AndI, Value *Op,
+                             unsigned Amount, bool isLeftShift, 
+                             unsigned DestReg);
 
     /// emitBinaryConstOperation - Used by several functions to emit simple
     /// arithmetic and logical operations with constants on a register rather
@@ -359,8 +368,7 @@ namespace {
     ///
     void emitSimpleBinaryOperation(MachineBasicBlock *BB,
                                    MachineBasicBlock::iterator IP,
-                                   BinaryOperator *BO,
-                                   Value *Op0, Value *Op1,
+                                   BinaryOperator *BO, Value *Op0, Value *Op1,
                                    unsigned OperatorClass, unsigned TargetReg);
 
     /// emitBinaryFPOperation - This method handles emission of floating point
@@ -2021,23 +2029,23 @@ void PPC32ISel::visitIntrinsicCall(Intrinsic::ID ID, CallInst &CI) {
 /// Xor.
 ///
 void PPC32ISel::visitSimpleBinary(BinaryOperator &B, unsigned OperatorClass) {
+  if (std::find(SkipList.begin(), SkipList.end(), &B) != SkipList.end())
+    return;
+
   unsigned DestReg = getReg(B);
   MachineBasicBlock::iterator MI = BB->end();
-  Value *Op0 = B.getOperand(0), *Op1 = B.getOperand(1);
-  unsigned Class = getClassB(B.getType());
-
-  if (std::find(SkipList.begin(), SkipList.end(), &B) != SkipList.end())
+  RlwimiRec RR = InsertMap[&B];
+  if (RR.Target != 0) {
+    unsigned TargetReg = getReg(RR.Target, BB, MI);
+    unsigned InsertReg = getReg(RR.Insert, BB, MI);
+    BuildMI(*BB, MI, PPC::RLWIMI, 5, DestReg).addReg(TargetReg)
+      .addReg(InsertReg).addImm(RR.Shift).addImm(RR.MB).addImm(RR.ME);
     return;
-  
-  RlwimiRec r = RlwimiMap[&B];
-  if (0 != r.Op0) {
-    unsigned Op0Reg = getReg(r.Op0, BB, MI);
-    unsigned Op1Reg = getReg(r.Op1, BB, MI);
-    BuildMI(*BB, MI, PPC::RLWIMI, 5, DestReg).addReg(Op1Reg)
-            .addReg(Op0Reg).addImm(r.Shift).addImm(r.MB).addImm(r.ME);
-  } else {
-    emitSimpleBinaryOperation(BB, MI, &B, Op0, Op1, OperatorClass, DestReg);
   }
+    
+  unsigned Class = getClassB(B.getType());
+  Value *Op0 = B.getOperand(0), *Op1 = B.getOperand(1);
+  emitSimpleBinaryOperation(BB, MI, &B, Op0, Op1, OperatorClass, DestReg);
 }
 
 /// emitBinaryFPOperation - This method handles emission of floating point
@@ -2134,50 +2142,187 @@ static bool isRunOfOnes(unsigned Val, unsigned &MB, unsigned &ME) {
   return false;
 }
 
-/// emitBitfieldInsert - return true if we were able to fold the sequence of
-/// instructions starting with AndI into a bitfield insert.
-bool PPC32ISel::emitBitfieldInsert(BinaryOperator *AndI, unsigned ShlAmount, 
-                                   Value *InsertOp) {
-  if (AndI->hasOneUse()) {
-    ConstantInt *CI_1 = dyn_cast<ConstantInt>(AndI->getOperand(1));
-    BinaryOperator *OrI = dyn_cast<BinaryOperator>(*(AndI->use_begin()));
-    if (CI_1 && OrI && OrI->getOpcode() == Instruction::Or) {
-      Value *Op0 = OrI->getOperand(0);
-      Value *Op1 = OrI->getOperand(1);
-      BinaryOperator *AndI_2 = 0;
-      // Whichever operand our initial And instruction is to the Or instruction,
-      // Look at the other operand to determine if it is also an And instruction
-      if (AndI == Op0) { 
-        AndI_2 = dyn_cast<BinaryOperator>(Op1);
-      }  else if (AndI == Op1) {
-        AndI_2 = dyn_cast<BinaryOperator>(Op0);
-        std::swap(Op0, Op1);
-      } else {
-        assert(0 && "And instruction not used in Or!");
-      }
-      // Verify that the second operand to the Or is an And with one use
-      if (AndI_2 && AndI_2->hasOneUse() 
-                 && AndI_2->getOpcode() == Instruction::And) {
-        // Check to see if this And instruction also has a constant int operand.
-        // If it does, then we can replace this sequence of instructions with an
-        // insert if the sum of the two ConstantInts has all bits set, and
-        // one is a run of ones (which implies the other is as well).
-        ConstantInt *CI_2 = dyn_cast<ConstantInt>(AndI_2->getOperand(1));
-        if (CI_2) { 
-          unsigned Imm1 = CI_1->getRawValue();
-          unsigned Imm2 = CI_2->getRawValue();
-          if (Imm1 + Imm2 == 0xFFFFFFFF) {
-            unsigned MB, ME;
-            if (isRunOfOnes(Imm1, MB, ME)) {
-              ++Bitfields;
-              SkipList.push_back(AndI);
-              SkipList.push_back(AndI_2);
-              RlwimiMap[OrI] = RlwimiRec(ShlAmount, MB, ME, 
-                InsertOp, AndI_2->getOperand(0));
-              return true;
+/// isInsertAndHalf - Helper function for emitBitfieldInsert.  Returns true if
+/// OpUser has one use, is used by an or instruction, and is itself an and whose
+/// second operand is a constant int.  Optionally, set OrI to the Or instruction
+/// that is the sole user of OpUser, and Op1User to the other operand of the Or
+/// instruction.
+static bool isInsertAndHalf(User *OpUser, Instruction **Op1User, 
+                            Instruction **OrI, unsigned &Mask) {
+  // If this instruction doesn't have one use, then return false.
+  if (!OpUser->hasOneUse())
+    return false;
+  
+  Mask = 0xFFFFFFFF;
+  if (BinaryOperator *BO = dyn_cast<BinaryOperator>(OpUser))
+    if (BO->getOpcode() == Instruction::And) {
+      Value *AndUse = *(OpUser->use_begin());
+      if (BinaryOperator *Or = dyn_cast<BinaryOperator>(AndUse)) {
+        if (Or->getOpcode() == Instruction::Or) {
+          if (ConstantInt *CI = dyn_cast<ConstantInt>(OpUser->getOperand(1))) {
+            if (OrI) *OrI = Or;
+            if (Op1User) {
+              if (Or->getOperand(0) == OpUser)
+                *Op1User = dyn_cast<Instruction>(Or->getOperand(1));
+              else
+                *Op1User = dyn_cast<Instruction>(Or->getOperand(0));
             }
+            Mask &= CI->getRawValue();
+            return true;
+          }
+        }
+      }
+    }
+  return false;
+}
+
+/// isInsertShiftHalf - Helper function for emitBitfieldInsert.  Returns true if
+/// OpUser has one use, is used by an or instruction, and is itself a shift
+/// instruction that is either used directly by the or instruction, or is used
+/// by an and instruction whose second operand is a constant int, and which is
+/// used by the or instruction.
+static bool isInsertShiftHalf(User *OpUser, Instruction **Op1User, 
+                              Instruction **OrI, Instruction **OptAndI, 
+                              unsigned &Shift, unsigned &Mask) {
+  // If this instruction doesn't have one use, then return false.
+  if (!OpUser->hasOneUse())
+    return false;
+  
+  Mask = 0xFFFFFFFF;
+  if (ShiftInst *SI = dyn_cast<ShiftInst>(OpUser)) {
+    if (ConstantInt *CI = dyn_cast<ConstantInt>(SI->getOperand(1))) {
+      Shift = CI->getRawValue();
+      if (SI->getOpcode() == Instruction::Shl)
+        Mask <<= Shift;
+      else if (!SI->getOperand(0)->getType()->isSigned()) {
+        Mask >>= Shift;
+        Shift = 32 - Shift;
+      }
+
+      // Now check to see if the shift instruction is used by an or.
+      Value *ShiftUse = *(OpUser->use_begin());
+      Value *OptAndICopy = 0;
+      if (BinaryOperator *BO = dyn_cast<BinaryOperator>(ShiftUse)) {
+        if (BO->getOpcode() == Instruction::And && BO->hasOneUse()) {
+          if (ConstantInt *ACI = dyn_cast<ConstantInt>(BO->getOperand(1))) {
+            if (OptAndI) *OptAndI = BO;
+            OptAndICopy = BO;
+            Mask &= ACI->getRawValue();
+            BO = dyn_cast<BinaryOperator>(*(BO->use_begin()));
           }
         }
+        if (BO && BO->getOpcode() == Instruction::Or) {
+          if (OrI) *OrI = BO;
+          if (Op1User) {
+            if (BO->getOperand(0) == OpUser || BO->getOperand(0) == OptAndICopy)
+              *Op1User = dyn_cast<Instruction>(BO->getOperand(1));
+            else
+              *Op1User = dyn_cast<Instruction>(BO->getOperand(0));
+          }
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
+
+/// emitBitfieldInsert - turn a shift used only by an and with immediate into 
+/// the rotate left word immediate then mask insert (rlwimi) instruction.
+/// Patterns matched:
+/// 1. or shl, and   5. or (shl-and), and   9. or and, and
+/// 2. or and, shl   6. or and, (shl-and)
+/// 3. or shr, and   7. or (shr-and), and
+/// 4. or and, shr   8. or and, (shr-and)
+bool PPC32ISel::emitBitfieldInsert(MachineBasicBlock *MBB, 
+                                   MachineBasicBlock::iterator IP,
+                                   User *OpUser, unsigned DestReg) {
+  // Instructions to skip if we match any of the patterns
+  Instruction *Op0User, *Op1User = 0, *OptAndI = 0, *OrI = 0;
+  unsigned TgtMask, InsMask, Amount = 0;
+  bool matched = false;
+
+  // We require OpUser to be an instruction to continue
+  Op0User = dyn_cast<Instruction>(OpUser);
+  if (0 == Op0User)
+    return false;
+
+  // Look for cases 2, 4, 6, 8, and 9
+  if (isInsertAndHalf(Op0User, &Op1User, &OrI, TgtMask))
+    if (Op1User)
+      if (isInsertAndHalf(Op1User, 0, 0, InsMask))
+        matched = true;
+      else if (isInsertShiftHalf(Op1User, 0, 0, &OptAndI, Amount, InsMask))
+        matched = true;
+  
+  // Look for cases 1, 3, 5, and 7.  Force the shift argument to be the one
+  // inserted into the target, since rlwimi can only rotate the value inserted,
+  // not the value being inserted into.
+  if (matched == false)
+    if (isInsertShiftHalf(Op0User, &Op1User, &OrI, &OptAndI, Amount, InsMask))
+      if (Op1User && isInsertAndHalf(Op1User, 0, 0, TgtMask)) {
+        std::swap(Op0User, Op1User);
+        matched = true;
+      }
+  
+  // We didn't succeed in matching one of the patterns, so return false
+  if (matched == false)
+    return false;
+  
+  // If the masks xor to -1, and the insert mask is a run of ones, then we have
+  // succeeded in matching one of the cases for generating rlwimi.  Update the
+  // skip lists and users of the Instruction::Or.
+  unsigned MB, ME;
+  if (((TgtMask ^ InsMask) == 0xFFFFFFFF) && isRunOfOnes(InsMask, MB, ME)) {
+    SkipList.push_back(Op0User);
+    SkipList.push_back(Op1User);
+    SkipList.push_back(OptAndI);
+    InsertMap[OrI] = RlwimiRec(Op0User->getOperand(0), Op1User->getOperand(0), 
+                               Amount, MB, ME);
+    return true;
+  }
+  return false;
+}
+
+/// emitBitfieldExtract - turn a shift used only by an and with immediate into the
+/// rotate left word immediate then and with mask (rlwinm) instruction.
+bool PPC32ISel::emitBitfieldExtract(MachineBasicBlock *MBB, 
+                                    MachineBasicBlock::iterator IP,
+                                    BinaryOperator *BO, Value *Op,
+                                    unsigned Amount, bool isLeftShift,
+                                    unsigned DestReg) {
+  return false;
+  if (BO && BO->getOpcode() == Instruction::And) {
+    // Since the powerpc shift instructions are really rotate left, subtract 32
+    // to simulate a right shift.
+    unsigned Rotate = (isLeftShift) ? Amount : 32 - Amount;
+
+    if (ConstantInt *CI = dyn_cast<ConstantInt>(BO->getOperand(1))) {
+      unsigned Imm = CI->getRawValue(), MB, ME;
+      // In the case of a left shift or unsigned right shift, be sure to clear
+      // any bits that would have been zeroes shifted in from the left or right.
+      // Usually instcombine will do this for us, but there's no guarantee the 
+      // optimization has been performed already.
+      // 
+      // Also, take this opportunity to check for the algebraic right shift case
+      // where the mask would overlap sign bits shifted in from the left.  We do
+      // not want to perform the optimization in that case since we could clear
+      // bits that should be set. Think of int (x >> 17) & 0x0000FFFF;
+      unsigned mask = 0xFFFFFFFF;
+      if (isLeftShift)
+        Imm &= (mask << Amount);
+      else if (!isLeftShift && !Op->getType()->isSigned())
+        Imm &= (mask >> Amount);
+      else if (((mask << Rotate) & Imm) != 0)
+        return false;
+        
+      if (isRunOfOnes(Imm, MB, ME)) {
+        unsigned SrcReg = getReg(Op, MBB, IP);
+        BuildMI(*MBB, IP, PPC::RLWINM, 4, DestReg).addReg(SrcReg)
+          .addImm(Rotate).addImm(MB).addImm(ME);
+        BO->replaceAllUsesWith(Op->use_back());
+        SkipList.push_back(BO);
+        return true;
       }
     }
   }
@@ -2322,9 +2467,9 @@ void PPC32ISel::emitSimpleBinaryOperation(MachineBasicBlock *MBB,
   // Special case: op Reg, <const int>
   if (ConstantInt *CI = dyn_cast<ConstantInt>(Op1))
     if (Class != cLong) {
-      if (OperatorClass == 2 && emitBitfieldInsert(BO, 0, Op0))
+      if (emitBitfieldInsert(MBB, IP, BO, DestReg))
         return;
-
+      
       unsigned Op0r = getReg(Op0, MBB, IP);
       emitBinaryConstOperation(MBB, IP, Op0r, CI, OperatorClass, DestReg);
       return;
@@ -2619,6 +2764,9 @@ void PPC32ISel::emitDivRemOperation(MachineBasicBlock *MBB,
 /// because the shift amount has to be in CL, not just any old register.
 ///
 void PPC32ISel::visitShiftInst(ShiftInst &I) {
+  if (std::find(SkipList.begin(), SkipList.end(), &I) != SkipList.end())
+    return;
+
   MachineBasicBlock::iterator IP = BB->end();
   emitShiftOperation(BB, IP, I.getOperand(0), I.getOperand(1),
                      I.getOpcode() == Instruction::Shl, I.getType(),
@@ -2776,17 +2924,16 @@ void PPC32ISel::emitShiftOperation(MachineBasicBlock *MBB,
     assert(CUI->getType() == Type::UByteTy && "Shift amount not a ubyte?");
     unsigned Amount = CUI->getValue();
     
-    // If this is a left shift with one use, and that use is an And instruction,
-    // then attempt to emit a bitfield insert.
-    if (isLeftShift) {
-      User *U = Op->use_back();
-      if (U->hasOneUse()) {
-        Value *V = *(U->use_begin());
-        BinaryOperator *BO = dyn_cast<BinaryOperator>(V);
-        if (BO && BO->getOpcode() == Instruction::And) {
-          if (emitBitfieldInsert(BO, Amount, Op))
-            return;
-        }
+    // If this is a shift with one use, and that use is an And instruction,
+    // then attempt to emit a bitfield operation.
+    User *U = Op->use_back();
+    if (U->hasOneUse()) {
+      BinaryOperator *BO = dyn_cast<BinaryOperator>(*(U->use_begin()));
+      if (BO) {
+        if (emitBitfieldInsert(MBB, IP, U, DestReg))
+          return;
+        if (emitBitfieldExtract(MBB, IP, BO, Op, Amount, isLeftShift, DestReg))
+          return;
       }
     }