Teach DAGCombine to fold constant offsets into GlobalAddress nodes,
and add a TargetLowering hook for it to use to determine when this
is legal (i.e. not in PIC mode, etc.)

This allows instruction selection to emit folded constant offsets
in more cases, such as the included testcase, eliminating the need
for explicit arithmetic instructions.

This eliminates the need for the C++ code in X86ISelDAGToDAG.cpp
that attempted to achieve the same effect, but wasn't as effective.

Also, fix handling of offsets in GlobalAddressSDNodes in several
places, including changing GlobalAddressSDNode's offset from
int to int64_t.

The Mips, Alpha, Sparc, and CellSPU targets appear to be
unaware of GlobalAddress offsets currently, so set the hook to
false on those targets.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@57748 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Target/Alpha/AlphaISelLowering.cpp b/lib/Target/Alpha/AlphaISelLowering.cpp
index 33445f0..7878830 100644
--- a/lib/Target/Alpha/AlphaISelLowering.cpp
+++ b/lib/Target/Alpha/AlphaISelLowering.cpp
@@ -764,3 +764,9 @@
 
   return sinkMBB;
 }
+
+bool
+AlphaTargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const {
+  // The Alpha target isn't yet aware of offsets.
+  return false;
+}
diff --git a/lib/Target/Alpha/AlphaISelLowering.h b/lib/Target/Alpha/AlphaISelLowering.h
index 0a78997..3e870af 100644
--- a/lib/Target/Alpha/AlphaISelLowering.h
+++ b/lib/Target/Alpha/AlphaISelLowering.h
@@ -95,6 +95,8 @@
     MachineBasicBlock *EmitInstrWithCustomInserter(MachineInstr *MI,
                                                    MachineBasicBlock *BB);
 
+    virtual bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const;
+
   private:
     // Helpers for custom lowering.
     void LowerVAARG(SDNode *N, SDValue &Chain, SDValue &DataPtr,
diff --git a/lib/Target/CellSPU/SPUISelLowering.cpp b/lib/Target/CellSPU/SPUISelLowering.cpp
index 086a25f..659ba75 100644
--- a/lib/Target/CellSPU/SPUISelLowering.cpp
+++ b/lib/Target/CellSPU/SPUISelLowering.cpp
@@ -3058,3 +3058,9 @@
 bool SPUTargetLowering::isLegalAddressImmediate(llvm::GlobalValue* GV) const {
   return false;
 }
+
+bool
+SPUTargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const {
+  // The SPU target isn't yet aware of offsets.
+  return false;
+}
diff --git a/lib/Target/CellSPU/SPUISelLowering.h b/lib/Target/CellSPU/SPUISelLowering.h
index e79b136..54aac66 100644
--- a/lib/Target/CellSPU/SPUISelLowering.h
+++ b/lib/Target/CellSPU/SPUISelLowering.h
@@ -139,6 +139,8 @@
     /// as the offset of the target addressing mode.
     virtual bool isLegalAddressImmediate(int64_t V, const Type *Ty) const;
     virtual bool isLegalAddressImmediate(GlobalValue *) const;
+
+    virtual bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const;
   };
 }
 
diff --git a/lib/Target/Mips/MipsISelLowering.cpp b/lib/Target/Mips/MipsISelLowering.cpp
index 3224d73..8fdc4b7 100644
--- a/lib/Target/Mips/MipsISelLowering.cpp
+++ b/lib/Target/Mips/MipsISelLowering.cpp
@@ -1094,3 +1094,9 @@
   }
   return std::vector<unsigned>();
 }
+
+bool
+MipsTargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const {
+  // The Mips target isn't yet aware of offsets.
+  return false;
+}
diff --git a/lib/Target/Mips/MipsISelLowering.h b/lib/Target/Mips/MipsISelLowering.h
index b19ce58..5704b7e 100644
--- a/lib/Target/Mips/MipsISelLowering.h
+++ b/lib/Target/Mips/MipsISelLowering.h
@@ -118,6 +118,8 @@
     std::vector<unsigned>
     getRegClassForInlineAsmConstraint(const std::string &Constraint,
               MVT VT) const;
+
+    virtual bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const;
   };
 }
 
diff --git a/lib/Target/Sparc/SparcISelLowering.cpp b/lib/Target/Sparc/SparcISelLowering.cpp
index e81688e..59e19d5 100644
--- a/lib/Target/Sparc/SparcISelLowering.cpp
+++ b/lib/Target/Sparc/SparcISelLowering.cpp
@@ -996,3 +996,9 @@
 
   return std::vector<unsigned>();
 }
+
+bool
+SparcTargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const {
+  // The Sparc target isn't yet aware of offsets.
+  return false;
+}
diff --git a/lib/Target/Sparc/SparcISelLowering.h b/lib/Target/Sparc/SparcISelLowering.h
index ea3171d..a0e3c65 100644
--- a/lib/Target/Sparc/SparcISelLowering.h
+++ b/lib/Target/Sparc/SparcISelLowering.h
@@ -70,6 +70,8 @@
     std::vector<unsigned>
     getRegClassForInlineAsmConstraint(const std::string &Constraint,
                                       MVT VT) const;
+
+    virtual bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const;
   };
 } // end namespace llvm
 
diff --git a/lib/Target/X86/X86ISelDAGToDAG.cpp b/lib/Target/X86/X86ISelDAGToDAG.cpp
index cf83e68..6735f1d 100644
--- a/lib/Target/X86/X86ISelDAGToDAG.cpp
+++ b/lib/Target/X86/X86ISelDAGToDAG.cpp
@@ -759,6 +759,7 @@
 /// addressing mode.
 bool X86DAGToDAGISel::MatchAddress(SDValue N, X86ISelAddressMode &AM,
                                    bool isRoot, unsigned Depth) {
+  bool is64Bit = Subtarget->is64Bit();
   DOUT << "MatchAddress: "; DEBUG(AM.dump());
   // Limit recursion.
   if (Depth > 5)
@@ -768,7 +769,7 @@
   if (AM.isRIPRel) {
     if (!AM.ES && AM.JT != -1 && N.getOpcode() == ISD::Constant) {
       int64_t Val = cast<ConstantSDNode>(N)->getSExtValue();
-      if (isInt32(AM.Disp + Val)) {
+      if (!is64Bit || isInt32(AM.Disp + Val)) {
         AM.Disp += Val;
         return false;
       }
@@ -783,7 +784,7 @@
   default: break;
   case ISD::Constant: {
     int64_t Val = cast<ConstantSDNode>(N)->getSExtValue();
-    if (isInt32(AM.Disp + Val)) {
+    if (!is64Bit || isInt32(AM.Disp + Val)) {
       AM.Disp += Val;
       return false;
     }
@@ -791,10 +792,9 @@
   }
 
   case X86ISD::Wrapper: {
-DOUT << "Wrapper: 64bit " << Subtarget->is64Bit();
-DOUT << " AM "; DEBUG(AM.dump()); DOUT << "\n";
-DOUT << "AlreadySelected " << AlreadySelected << "\n";
-    bool is64Bit = Subtarget->is64Bit();
+    DOUT << "Wrapper: 64bit " << is64Bit;
+    DOUT << " AM "; DEBUG(AM.dump()); DOUT << "\n";
+    DOUT << "AlreadySelected " << AlreadySelected << "\n";
     // Under X86-64 non-small code model, GV (and friends) are 64-bits.
     // Also, base and index reg must be 0 in order to use rip as base.
     if (is64Bit && (TM.getCodeModel() != CodeModel::Small ||
@@ -808,17 +808,21 @@
     if (!AlreadySelected || (AM.Base.Reg.getNode() && AM.IndexReg.getNode())) {
       SDValue N0 = N.getOperand(0);
       if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(N0)) {
-        GlobalValue *GV = G->getGlobal();
-        AM.GV = GV;
-        AM.Disp += G->getOffset();
-        AM.isRIPRel = TM.symbolicAddressesAreRIPRel();
-        return false;
+        if (!is64Bit || isInt32(AM.Disp + G->getOffset())) {
+          GlobalValue *GV = G->getGlobal();
+          AM.GV = GV;
+          AM.Disp += G->getOffset();
+          AM.isRIPRel = TM.symbolicAddressesAreRIPRel();
+          return false;
+        }
       } else if (ConstantPoolSDNode *CP = dyn_cast<ConstantPoolSDNode>(N0)) {
-        AM.CP = CP->getConstVal();
-        AM.Align = CP->getAlignment();
-        AM.Disp += CP->getOffset();
-        AM.isRIPRel = TM.symbolicAddressesAreRIPRel();
-        return false;
+        if (!is64Bit || isInt32(AM.Disp + CP->getOffset())) {
+          AM.CP = CP->getConstVal();
+          AM.Align = CP->getAlignment();
+          AM.Disp += CP->getOffset();
+          AM.isRIPRel = TM.symbolicAddressesAreRIPRel();
+          return false;
+        }
       } else if (ExternalSymbolSDNode *S =dyn_cast<ExternalSymbolSDNode>(N0)) {
         AM.ES = S->getSymbol();
         AM.isRIPRel = TM.symbolicAddressesAreRIPRel();
@@ -862,7 +866,7 @@
           ConstantSDNode *AddVal =
             cast<ConstantSDNode>(ShVal.getNode()->getOperand(1));
           uint64_t Disp = AM.Disp + (AddVal->getZExtValue() << Val);
-          if (isInt32(Disp))
+          if (!is64Bit || isInt32(Disp))
             AM.Disp = Disp;
           else
             AM.IndexReg = ShVal;
@@ -905,7 +909,7 @@
               cast<ConstantSDNode>(MulVal.getNode()->getOperand(1));
             uint64_t Disp = AM.Disp + AddVal->getZExtValue() *
                                       CN->getZExtValue();
-            if (isInt32(Disp))
+            if (!is64Bit || isInt32(Disp))
               AM.Disp = Disp;
             else
               Reg = N.getNode()->getOperand(0);
@@ -944,7 +948,7 @@
           // Address could not have picked a GV address for the displacement.
           AM.GV == NULL &&
           // On x86-64, the resultant disp must fit in 32-bits.
-          isInt32(AM.Disp + CN->getSExtValue()) &&
+          (!is64Bit || isInt32(AM.Disp + CN->getSExtValue())) &&
           // Check to see if the LHS & C is zero.
           CurDAG->MaskedValueIsZero(N.getOperand(0), CN->getAPIntValue())) {
         AM.Disp += CN->getZExtValue();
@@ -1248,49 +1252,6 @@
     case X86ISD::GlobalBaseReg: 
       return getGlobalBaseReg();
 
-    case ISD::ADD: {
-      // Turn ADD X, c to MOV32ri X+c. This cannot be done with tblgen'd
-      // code and is matched first so to prevent it from being turned into
-      // LEA32r X+c.
-      // In 64-bit small code size mode, use LEA to take advantage of
-      // RIP-relative addressing.
-      if (TM.getCodeModel() != CodeModel::Small)
-        break;
-      MVT PtrVT = TLI.getPointerTy();
-      SDValue N0 = N.getOperand(0);
-      SDValue N1 = N.getOperand(1);
-      if (N.getNode()->getValueType(0) == PtrVT &&
-          N0.getOpcode() == X86ISD::Wrapper &&
-          N1.getOpcode() == ISD::Constant) {
-        unsigned Offset = (unsigned)cast<ConstantSDNode>(N1)->getZExtValue();
-        SDValue C(0, 0);
-        // TODO: handle ExternalSymbolSDNode.
-        if (GlobalAddressSDNode *G =
-            dyn_cast<GlobalAddressSDNode>(N0.getOperand(0))) {
-          C = CurDAG->getTargetGlobalAddress(G->getGlobal(), PtrVT,
-                                             G->getOffset() + Offset);
-        } else if (ConstantPoolSDNode *CP =
-                   dyn_cast<ConstantPoolSDNode>(N0.getOperand(0))) {
-          C = CurDAG->getTargetConstantPool(CP->getConstVal(), PtrVT,
-                                            CP->getAlignment(),
-                                            CP->getOffset()+Offset);
-        }
-
-        if (C.getNode()) {
-          if (Subtarget->is64Bit()) {
-            SDValue Ops[] = { CurDAG->getRegister(0, PtrVT), getI8Imm(1),
-                                CurDAG->getRegister(0, PtrVT), C };
-            return CurDAG->SelectNodeTo(N.getNode(), X86::LEA64r,
-                                        MVT::i64, Ops, 4);
-          } else
-            return CurDAG->SelectNodeTo(N.getNode(), X86::MOV32ri, PtrVT, C);
-        }
-      }
-
-      // Other cases are handled by auto-generated code.
-      break;
-    }
-
     case X86ISD::ATOMOR64_DAG:
       return SelectAtomic64(Node, X86::ATOMOR6432);
     case X86ISD::ATOMXOR64_DAG:
diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp
index fa46769..c977dbe 100644
--- a/lib/Target/X86/X86ISelLowering.cpp
+++ b/lib/Target/X86/X86ISelLowering.cpp
@@ -1705,7 +1705,8 @@
     // non-JIT mode.
     if (!Subtarget->GVRequiresExtraLoad(G->getGlobal(),
                                         getTargetMachine(), true))
-      Callee = DAG.getTargetGlobalAddress(G->getGlobal(), getPointerTy());
+      Callee = DAG.getTargetGlobalAddress(G->getGlobal(), getPointerTy(),
+                                          G->getOffset());
   } else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) {
     Callee = DAG.getTargetExternalSymbol(S->getSymbol(), getPointerTy());
   } else if (IsTailCall) {
@@ -4390,12 +4391,24 @@
 
 SDValue
 X86TargetLowering::LowerGlobalAddress(const GlobalValue *GV,
+                                      int64_t Offset,
                                       SelectionDAG &DAG) const {
-  SDValue Result = DAG.getTargetGlobalAddress(GV, getPointerTy());
+  bool IsPic = getTargetMachine().getRelocationModel() == Reloc::PIC_;
+  bool ExtraLoadRequired =
+    Subtarget->GVRequiresExtraLoad(GV, getTargetMachine(), false);
+
+  // Create the TargetGlobalAddress node, folding in the constant
+  // offset if it is legal.
+  SDValue Result;
+  if (!IsPic && !ExtraLoadRequired) {
+    Result = DAG.getTargetGlobalAddress(GV, getPointerTy(), Offset);
+    Offset = 0;
+  } else
+    Result = DAG.getTargetGlobalAddress(GV, getPointerTy(), 0);
   Result = DAG.getNode(X86ISD::Wrapper, getPointerTy(), Result);
+
   // With PIC, the address is actually $g + Offset.
-  if (getTargetMachine().getRelocationModel() == Reloc::PIC_ &&
-      !Subtarget->isPICStyleRIPRel()) {
+  if (IsPic && !Subtarget->isPICStyleRIPRel()) {
     Result = DAG.getNode(ISD::ADD, getPointerTy(),
                          DAG.getNode(X86ISD::GlobalBaseReg, getPointerTy()),
                          Result);
@@ -4406,17 +4419,24 @@
   // the GlobalAddress must be in the base or index register of the address, not
   // the GV offset field. Platform check is inside GVRequiresExtraLoad() call
   // The same applies for external symbols during PIC codegen
-  if (Subtarget->GVRequiresExtraLoad(GV, getTargetMachine(), false))
+  if (ExtraLoadRequired)
     Result = DAG.getLoad(getPointerTy(), DAG.getEntryNode(), Result,
                          PseudoSourceValue::getGOT(), 0);
 
+  // If there was a non-zero offset that we didn't fold, create an explicit
+  // addition for it.
+  if (Offset != 0)
+    Result = DAG.getNode(ISD::ADD, getPointerTy(), Result,
+                         DAG.getConstant(Offset, getPointerTy()));
+
   return Result;
 }
 
 SDValue
 X86TargetLowering::LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) {
   const GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal();
-  return LowerGlobalAddress(GV, DAG);
+  int64_t Offset = cast<GlobalAddressSDNode>(Op)->getOffset();
+  return LowerGlobalAddress(GV, Offset, DAG);
 }
 
 // Lower ISD::GlobalTLSAddress using the "general dynamic" model, 32 bit
@@ -7006,6 +7026,7 @@
   if (N->getOpcode() == X86ISD::Wrapper) {
     if (isa<GlobalAddressSDNode>(N->getOperand(0))) {
       GA = cast<GlobalAddressSDNode>(N->getOperand(0))->getGlobal();
+      Offset = cast<GlobalAddressSDNode>(N->getOperand(0))->getOffset();
       return true;
     }
   }
@@ -7448,7 +7469,7 @@
     
     if (GA) {
       if (hasMemory) 
-        Op = LowerGlobalAddress(GA->getGlobal(), DAG);
+        Op = LowerGlobalAddress(GA->getGlobal(), Offset, DAG);
       else
         Op = DAG.getTargetGlobalAddress(GA->getGlobal(), GA->getValueType(0),
                                         Offset);
diff --git a/lib/Target/X86/X86ISelLowering.h b/lib/Target/X86/X86ISelLowering.h
index b3d1654..b76ba84 100644
--- a/lib/Target/X86/X86ISelLowering.h
+++ b/lib/Target/X86/X86ISelLowering.h
@@ -551,7 +551,8 @@
     SDValue LowerINSERT_VECTOR_ELT_SSE4(SDValue Op, SelectionDAG &DAG);
     SDValue LowerSCALAR_TO_VECTOR(SDValue Op, SelectionDAG &DAG);
     SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG);
-    SDValue LowerGlobalAddress(const GlobalValue *GV, SelectionDAG &DAG) const;
+    SDValue LowerGlobalAddress(const GlobalValue *GV, int64_t Offset,
+                               SelectionDAG &DAG) const;
     SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG);
     SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG);
     SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG);
diff --git a/lib/Target/X86/X86TargetMachine.cpp b/lib/Target/X86/X86TargetMachine.cpp
index 923823b..ef4f897 100644
--- a/lib/Target/X86/X86TargetMachine.cpp
+++ b/lib/Target/X86/X86TargetMachine.cpp
@@ -143,6 +143,15 @@
     else
       setRelocationModel(Reloc::Static);
   }
+
+  // ELF doesn't have a distinct dynamic-no-PIC model. Dynamic-no-PIC
+  // is defined as a model for code which may be used in static or
+  // dynamic executables but not necessarily a shared library. On ELF
+  // implement this by using the Static model.
+  if (Subtarget.isTargetELF() &&
+      getRelocationModel() == Reloc::DynamicNoPIC)
+    setRelocationModel(Reloc::Static);
+
   if (Subtarget.is64Bit()) {
     // No DynamicNoPIC support under X86-64.
     if (getRelocationModel() == Reloc::DynamicNoPIC)