Index: llvm/docs/Statepoints.rst
===================================================================
--- llvm/docs/Statepoints.rst
+++ llvm/docs/Statepoints.rst
@@ -433,6 +433,76 @@
 
 .. _statepoint-stackmap-format:
 
+'llvm.experimental.gc.find.base' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+      declare <pointer type>
+        @llvm.experimental.gc.get.pointer.base(
+          <pointer type> readnone nocapture %derived_ptr)
+          nounwind readnone willreturn "gc-leaf-function"
+
+Overview:
+"""""""""
+
+``gc.get.pointer.base`` for a derived pointer returns its base pointer.
+
+Operands:
+"""""""""
+
+The only argument is an object pointer.
+
+Semantics:
+""""""""""
+
+This intrinsic is inlined by the :ref:`RewriteStatepointsForGC` pass
+by replacing all uses of this callsite with the base pointer value.
+The replacement is done as part of the lowering to the explicit
+statepoint model.
+The return pointer type must be the same as the type of the parameter.
+
+
+'llvm.experimental.gc.get.pointer.offset' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+      declare i64
+        @llvm.experimental.gc.get.pointer.offset(
+          <pointer type> readnone nocapture %derived_ptr)
+          nounwind readnone willreturn "gc-leaf-function"
+
+Overview:
+"""""""""
+
+``gc.get.pointer.offset`` for a derived pointer returns the offset from its
+base pointer.
+
+Operands:
+"""""""""
+
+The only argument is an object pointer.
+
+Semantics:
+""""""""""
+
+This intrinsic is inlined by the :ref:`RewriteStatepointsForGC` pass by
+replacing all uses of this callsite with the offset of a derived pointer
+from its base pointer value. The replacement is done as part of the
+lowering to the explicit statepoint model.
+Basically this call calculates difference between the derived pointer and
+its base pointer (see :ref:`gc.get.pointer.base`) both ptrtoint casted. But
+this cast done outside the :ref:`RewriteStatepointsForGC` pass could result
+in the pointers lost for further lowering from the abstract model to the
+explicit physical one.
+
 Stack Map Format
 ================
 
Index: llvm/include/llvm/IR/IRBuilder.h
===================================================================
--- llvm/include/llvm/IR/IRBuilder.h
+++ llvm/include/llvm/IR/IRBuilder.h
@@ -850,6 +850,14 @@
                              Type *ResultType,
                              const Twine &Name = "");
 
+  /// Create a call to the experimental.gc.pointer.base intrinsic to get the
+  /// base pointer for the specified derived pointer.
+  CallInst *CreateGCGetPointerBase(Value *DerivedPtr, const Twine &Name = "");
+
+  /// Create a call to the experimental.gc.get.pointer.offset intrinsic to get
+  /// the offset of the specified derived pointer from its base.
+  CallInst *CreateGCGetPointerOffset(Value *DerivedPtr, const Twine &Name = "");
+
   /// Create a call to llvm.vscale, multiplied by \p Scaling. The type of VScale
   /// will be the same type as that of \p Scaling.
   Value *CreateVScale(Constant *Scaling, const Twine &Name = "");
Index: llvm/include/llvm/IR/Intrinsics.td
===================================================================
--- llvm/include/llvm/IR/Intrinsics.td
+++ llvm/include/llvm/IR/Intrinsics.td
@@ -60,6 +60,9 @@
 // Throws - This intrinsic can throw.
 def Throws : IntrinsicProperty;
 
+// GCLeafFunction - This intrinsic has attribute "gc-leaf-function".
+def IntrGCLeafFunction : IntrinsicProperty;
+
 // Attribute index needs to match `AttrIndex` defined `Attributes.h`.
 class AttrIndex<int idx> {
   int Value = idx;
@@ -1190,6 +1193,16 @@
                                              [IntrNoMem, ImmArg<ArgIndex<1>>,
                                               ImmArg<ArgIndex<2>>]>;
 
+def int_experimental_gc_get_pointer_base : Intrinsic<[llvm_anyptr_ty],
+                 [llvm_anyptr_ty], [IntrNoMem, IntrWillReturn,
+                 IntrGCLeafFunction, ReadNone<ArgIndex<0>>,
+                 NoCapture<ArgIndex<0>>]>;
+
+def int_experimental_gc_get_pointer_offset : Intrinsic<[llvm_i64_ty],
+                 [llvm_anyptr_ty], [IntrNoMem, IntrWillReturn,
+                 IntrGCLeafFunction, ReadNone<ArgIndex<0>>,
+                 NoCapture<ArgIndex<0>>]>;
+
 //===------------------------ Coroutine Intrinsics ---------------===//
 // These are documented in docs/Coroutines.rst
 
Index: llvm/lib/IR/IRBuilder.cpp
===================================================================
--- llvm/lib/IR/IRBuilder.cpp
+++ llvm/lib/IR/IRBuilder.cpp
@@ -778,6 +778,24 @@
  return createCallHelper(FnGCRelocate, Args, this, Name);
 }
 
+CallInst *IRBuilderBase::CreateGCGetPointerBase(Value *DerivedPtr,
+                                                const Twine &Name) {
+  Module *M = BB->getParent()->getParent();
+  Type *PtrTy = DerivedPtr->getType();
+  Function *FnGCFindBase = Intrinsic::getDeclaration(
+      M, Intrinsic::experimental_gc_get_pointer_base, {PtrTy, PtrTy});
+  return createCallHelper(FnGCFindBase, {DerivedPtr}, this, Name);
+}
+
+CallInst *IRBuilderBase::CreateGCGetPointerOffset(Value *DerivedPtr,
+                                                  const Twine &Name) {
+  Module *M = BB->getParent()->getParent();
+  Type *PtrTy = DerivedPtr->getType();
+  Function *FnGCGetOffset = Intrinsic::getDeclaration(
+      M, Intrinsic::experimental_gc_get_pointer_offset, {PtrTy});
+  return createCallHelper(FnGCGetOffset, {DerivedPtr}, this, Name);
+}
+
 CallInst *IRBuilderBase::CreateUnaryIntrinsic(Intrinsic::ID ID, Value *V,
                                               Instruction *FMFSource,
                                               const Twine &Name) {
Index: llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
===================================================================
--- llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
+++ llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
@@ -562,6 +562,8 @@
       // implications much.
       llvm_unreachable(
           "interaction with the gcroot mechanism is not supported");
+    case Intrinsic::experimental_gc_get_pointer_base:
+      return findBaseDefiningValue(II->getOperand(0));
     }
   }
   // We assume that functions in the source language only return base
@@ -594,6 +596,11 @@
   assert(!isa<InsertValueInst>(I) &&
          "Base pointer for a struct is meaningless");
 
+  // This value might have been generated by findBasePointer() called when
+  // substituting gc.get.pointer.base() intrinsic.
+  bool IsKnownBase =
+      isa<Instruction>(I) && cast<Instruction>(I)->getMetadata("is_base_value");
+
   // An extractelement produces a base result exactly when it's input does.
   // We may need to insert a parallel instruction to extract the appropriate
   // element out of the base vector corresponding to the input. Given this,
@@ -602,7 +609,7 @@
     // Note: There a lot of obvious peephole cases here.  This are deliberately
     // handled after the main base pointer inference algorithm to make writing
     // test cases to exercise that code easier.
-    return BaseDefiningValueResult(I, false);
+    return BaseDefiningValueResult(I, IsKnownBase);
 
   // The last two cases here don't return a base pointer.  Instead, they
   // return a value which dynamically selects from among several base
@@ -610,7 +617,7 @@
   // the caller to resolve these.
   assert((isa<SelectInst>(I) || isa<PHINode>(I)) &&
          "missing instruction case in findBaseDefiningValing");
-  return BaseDefiningValueResult(I, false);
+  return BaseDefiningValueResult(I, IsKnownBase);
 }
 
 /// Returns the base defining value for this value.
@@ -2364,9 +2371,55 @@
   }
 }
 
+static bool inlineGetBaseAndOffset(Function &F, DefiningValueMapTy &DVCache,
+                                   SmallVectorImpl<CallInst *> &Intrinsics) {
+  auto &Context = F.getContext();
+  auto &DL = F.getParent()->getDataLayout();
+  bool Changed = false;
+
+  for (auto *Callsite : Intrinsics)
+    if (Callsite->getIntrinsicID() ==
+        Intrinsic::experimental_gc_get_pointer_base) {
+      Changed = true;
+      Value *Base = findBasePointer(Callsite->getOperand(0), DVCache);
+      assert(!DVCache.count(Callsite));
+      auto *BaseBC = IRBuilder<>(Callsite).CreateBitCast(
+          Base, Callsite->getType(), suffixed_name_or(Base, ".cast", ""));
+      if (BaseBC != Base)
+        DVCache[BaseBC] = Base;
+      Callsite->replaceAllUsesWith(BaseBC);
+      if (!BaseBC->hasName())
+        BaseBC->takeName(Callsite);
+      Callsite->eraseFromParent();
+    } else if (Callsite->getIntrinsicID() ==
+               Intrinsic::experimental_gc_get_pointer_offset) {
+      Changed = true;
+      Value *Derived = Callsite->getOperand(0);
+      Value *Base = findBasePointer(Derived, DVCache);
+      assert(!DVCache.count(Callsite));
+      unsigned AddressSpace = Derived->getType()->getPointerAddressSpace();
+      unsigned IntPtrSize = DL.getPointerSizeInBits(AddressSpace);
+      IRBuilder<> Builder(Callsite);
+      Value *BaseInt =
+          Builder.CreatePtrToInt(Base, Type::getIntNTy(Context, IntPtrSize),
+                                 suffixed_name_or(Base, ".int", ""));
+      Value *DerivedInt =
+          Builder.CreatePtrToInt(Derived, Type::getIntNTy(Context, IntPtrSize),
+                                 suffixed_name_or(Derived, ".int", ""));
+      Value *Offset = Builder.CreateSub(DerivedInt, BaseInt);
+      Callsite->replaceAllUsesWith(Offset);
+      Offset->takeName(Callsite);
+      Callsite->eraseFromParent();
+    } else
+      llvm_unreachable("Unknown intrinsic");
+
+  return Changed;
+}
+
 static bool insertParsePoints(Function &F, DominatorTree &DT,
                               TargetTransformInfo &TTI,
-                              SmallVectorImpl<CallBase *> &ToUpdate) {
+                              SmallVectorImpl<CallBase *> &ToUpdate,
+                              SmallVectorImpl<CallInst *> &Intrinsics) {
 #ifndef NDEBUG
   // sanity check the input
   std::set<CallBase *> Uniqued;
@@ -2389,6 +2442,16 @@
     normalizeForInvokeSafepoint(II->getUnwindDest(), II->getParent(), DT);
   }
 
+  /* scope for caching */
+  // Cache the 'defining value' relation used in the computation and
+  // insertion of base phis and selects.  This ensures that we don't insert
+  // large numbers of duplicate base_phis.
+  DefiningValueMapTy DVCache;
+
+  // Inline @gc.get.pointer.base() and @gc.get.pointer.offset() before finding
+  // live references.
+  bool Inlined = inlineGetBaseAndOffset(F, DVCache, Intrinsics);
+
   // A list of dummy calls added to the IR to keep various values obviously
   // live in the IR.  We'll remove all of these when done.
   SmallVector<CallInst *, 64> Holders;
@@ -2417,17 +2480,12 @@
   findLiveReferences(F, DT, ToUpdate, Records);
 
   // B) Find the base pointers for each live pointer
-  /* scope for caching */ {
-    // Cache the 'defining value' relation used in the computation and
-    // insertion of base phis and selects.  This ensures that we don't insert
-    // large numbers of duplicate base_phis.
-    DefiningValueMapTy DVCache;
-
-    for (size_t i = 0; i < Records.size(); i++) {
-      PartiallyConstructedSafepointRecord &info = Records[i];
-      findBasePointers(DT, DVCache, ToUpdate[i], info);
-    }
-  } // end of cache scope
+  for (size_t i = 0; i < Records.size(); i++) {
+    PartiallyConstructedSafepointRecord &info = Records[i];
+    findBasePointers(DT, DVCache, ToUpdate[i], info);
+  }
+
+  DVCache.clear(); // end of cache scope
 
   // The base phi insertion logic (for any safepoint) may have inserted new
   // instructions which are now live at some safepoint.  The simplest such
@@ -2569,7 +2627,7 @@
 #endif
 
   relocationViaAlloca(F, DT, Live, Records);
-  return !Records.empty();
+  return Inlined || !Records.empty();
 }
 
 // Handles both return values and arguments for Functions and calls.
@@ -2755,6 +2813,7 @@
   // consider those in reachable code since we need to ask dominance queries
   // when rewriting.  We'll delete the unreachable ones in a moment.
   SmallVector<CallBase *, 64> ParsePointNeeded;
+  SmallVector<CallInst *, 64> Intrinsics;
   for (Instruction &I : instructions(F)) {
     // TODO: only the ones with the flag set!
     if (NeedsRewrite(I)) {
@@ -2766,10 +2825,14 @@
             "no unreachable blocks expected");
       ParsePointNeeded.push_back(cast<CallBase>(&I));
     }
+    if (auto *CI = dyn_cast<CallInst>(&I))
+      if (CI->getIntrinsicID() == Intrinsic::experimental_gc_get_pointer_base ||
+          CI->getIntrinsicID() == Intrinsic::experimental_gc_get_pointer_offset)
+        Intrinsics.emplace_back(CI);
   }
 
   // Return early if no work to do.
-  if (ParsePointNeeded.empty())
+  if (ParsePointNeeded.empty() && Intrinsics.empty())
     return MadeChange;
 
   // As a prepass, go ahead and aggressively destroy single entry phi nodes.
@@ -2838,7 +2901,7 @@
     }
   }
 
-  MadeChange |= insertParsePoints(F, DT, TTI, ParsePointNeeded);
+  MadeChange |= insertParsePoints(F, DT, TTI, ParsePointNeeded, Intrinsics);
   return MadeChange;
 }
 
Index: llvm/test/Transforms/RewriteStatepointsForGC/intrinsics.ll
===================================================================
--- /dev/null
+++ llvm/test/Transforms/RewriteStatepointsForGC/intrinsics.ll
@@ -0,0 +1,117 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
+; Use instcombine to cleanup offset computation.
+; Use gvn to remove duplicate computation.
+; RUN: opt -passes=rewrite-statepoints-for-gc,gvn,instcombine -S < %s | FileCheck %s
+
+target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128-p1:64:64"
+target triple = "x86_64-apple-macosx10.11.0"
+
+declare i8 addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1i8.p1i8(i8 addrspace(1)* readnone nocapture) nounwind readnone willreturn "gc-leaf-function"
+declare i8 addrspace(1)* addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1p1i8.p1p1i8(i8 addrspace(1)* addrspace(1)* readnone nocapture) nounwind readnone willreturn "gc-leaf-function"
+declare i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* readnone nocapture) nounwind readnone willreturn "gc-leaf-function"
+declare i64 @llvm.experimental.gc.get.pointer.offset.p1p1i8(i8 addrspace(1)* addrspace(1)* readnone nocapture) nounwind readnone willreturn "gc-leaf-function"
+
+declare void @foo() readonly
+
+define i8 addrspace(1)* addrspace(1)* @test_simple(i8 addrspace(1)* %obj1, i8 addrspace(1)* %obj2, i32 %len, i1 %c) gc "statepoint-example" {
+; CHECK-LABEL: define {{[^@]+}}@test_simple
+; CHECK-SAME: (i8 addrspace(1)* [[OBJ1:%.*]], i8 addrspace(1)* [[OBJ2:%.*]], i32 [[LEN:%.*]], i1 [[C:%.*]]) gc "statepoint-example" {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[OBJ1_12:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[OBJ1]], i64 12
+; CHECK-NEXT:    [[OBJ2_16:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[OBJ2]], i64 16
+; CHECK-NEXT:    [[OBJ_X_BASE1:%.*]] = select i1 [[C]], i8 addrspace(1)* [[OBJ1]], i8 addrspace(1)* [[OBJ2]], !is_base_value !0
+; CHECK-NEXT:    [[OBJ_X:%.*]] = select i1 [[C]], i8 addrspace(1)* [[OBJ1_12]], i8 addrspace(1)* [[OBJ2_16]]
+; CHECK-NEXT:    [[OBJ_Y_BASE:%.*]] = select i1 [[C]], i8 addrspace(1)* [[OBJ2]], i8 addrspace(1)* [[OBJ1]], !is_base_value !0
+; CHECK-NEXT:    [[OBJ_Y:%.*]] = select i1 [[C]], i8 addrspace(1)* [[OBJ2_16]], i8 addrspace(1)* [[OBJ1_12]]
+; CHECK-NEXT:    [[OBJ_YA:%.*]] = bitcast i8 addrspace(1)* [[OBJ_Y]] to i8 addrspace(1)* addrspace(1)*
+; CHECK-NEXT:    [[TMP0:%.*]] = ptrtoint i8 addrspace(1)* [[OBJ_X_BASE1]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = ptrtoint i8 addrspace(1)* [[OBJ_X]] to i64
+; CHECK-NEXT:    [[OBJ_X_OFFSET:%.*]] = sub i64 [[TMP1]], [[TMP0]]
+; CHECK-NEXT:    [[OBJ_YA_BASE:%.*]] = bitcast i8 addrspace(1)* [[OBJ_Y_BASE]] to i8 addrspace(1)* addrspace(1)*
+; CHECK-NEXT:    [[TMP2:%.*]] = ptrtoint i8 addrspace(1)* [[OBJ_Y_BASE]] to i64
+; CHECK-NEXT:    [[TMP3:%.*]] = ptrtoint i8 addrspace(1)* [[OBJ_Y]] to i64
+; CHECK-NEXT:    [[OBJ_YA_OFFSET:%.*]] = sub i64 [[TMP3]], [[TMP2]]
+; CHECK-NEXT:    [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* nonnull @foo, i32 0, i32 0, i32 0, i32 0) [ "deopt"(i8 addrspace(1)* [[OBJ_X_BASE1]], i64 [[OBJ_X_OFFSET]], i8 addrspace(1)* [[OBJ_X_BASE1]], i64 [[OBJ_X_OFFSET]], i8 addrspace(1)* addrspace(1)* [[OBJ_YA_BASE]], i64 [[OBJ_YA_OFFSET]]), "gc-live"(i8 addrspace(1)* addrspace(1)* [[OBJ_YA]], i8 addrspace(1)* [[OBJ_Y_BASE]]) ]
+; CHECK-NEXT:    [[OBJ_YA_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token [[STATEPOINT_TOKEN]], i32 1, i32 0)
+; CHECK-NEXT:    [[OBJ_YA_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_YA_RELOCATED]] to i8 addrspace(1)* addrspace(1)*
+; CHECK-NEXT:    ret i8 addrspace(1)* addrspace(1)* [[OBJ_YA_RELOCATED_CASTED]]
+;
+entry:
+  %obj1.12 = getelementptr inbounds i8, i8 addrspace(1)* %obj1, i64 12
+  %obj2.16 = getelementptr inbounds i8, i8 addrspace(1)* %obj2, i64 16
+  %obj.x = select i1 %c, i8 addrspace(1)* %obj1.12, i8 addrspace(1)* %obj2.16
+  %obj.y = select i1 %c, i8 addrspace(1)* %obj2.16, i8 addrspace(1)* %obj1.12
+  %obj.ya = bitcast i8 addrspace(1)* %obj.y to i8 addrspace(1)* addrspace(1)*
+  %obj.x.base = call i8 addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1i8.p1i8(i8 addrspace(1)* %obj.x)
+  %obj.x.offset = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x)
+  %obj.x.base2 = call i8 addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1i8.p1i8(i8 addrspace(1)* %obj.x)
+  %obj.x.offset2 = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x)
+  %obj.ya.base = call i8 addrspace(1)* addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1p1i8.p1p1i8(i8 addrspace(1)* addrspace(1)* %obj.ya)
+  %obj.ya.offset = call i64 @llvm.experimental.gc.get.pointer.offset.p1p1i8(i8 addrspace(1)* addrspace(1)* %obj.ya)
+  call void @foo() readonly [
+  "deopt"(i8 addrspace(1)* %obj.x.base, i64 %obj.x.offset, i8 addrspace(1)* %obj.x.base2, i64 %obj.x.offset2, i8 addrspace(1)* addrspace(1)* %obj.ya.base, i64 %obj.ya.offset) ]
+  ret i8 addrspace(1)* addrspace(1)* %obj.ya
+}
+
+define i8 addrspace(1)* @test_chained(i8 addrspace(1)* %obj1, i8 addrspace(1)* %obj2, i32 %len, i1 %c) gc "statepoint-example" {
+; CHECK-LABEL: define {{[^@]+}}@test_chained
+; CHECK-SAME: (i8 addrspace(1)* [[OBJ1:%.*]], i8 addrspace(1)* [[OBJ2:%.*]], i32 [[LEN:%.*]], i1 [[C:%.*]]) gc "statepoint-example" {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[OBJ_X_BASE1:%.*]] = select i1 [[C]], i8 addrspace(1)* [[OBJ1]], i8 addrspace(1)* [[OBJ2]], !is_base_value !0
+; CHECK-NEXT:    [[OBJ_X_BASE_GEP:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[OBJ_X_BASE1]], i64 8
+; CHECK-NEXT:    [[OBJ_X_BASE_OF_BASE_GEP:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[OBJ_X_BASE1]], i64 20
+; CHECK-NEXT:    [[OBJ_X_BASE_OF_BASE_OF_BASE_GEP:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[OBJ_X_BASE1]], i64 24
+; CHECK-NEXT:    [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* nonnull @foo, i32 0, i32 0, i32 0, i32 0) [ "deopt"(i8 addrspace(1)* [[OBJ_X_BASE1]], i8 addrspace(1)* [[OBJ_X_BASE1]], i8 addrspace(1)* [[OBJ_X_BASE1]], i8 addrspace(1)* [[OBJ_X_BASE_GEP]], i8 addrspace(1)* [[OBJ_X_BASE_OF_BASE_GEP]], i8 addrspace(1)* [[OBJ_X_BASE_OF_BASE_OF_BASE_GEP]], i8 addrspace(1)* [[OBJ_X_BASE1]], i8 addrspace(1)* [[OBJ_X_BASE1]], i8 addrspace(1)* [[OBJ_X_BASE1]], i64 0, i64 0, i64 0, i64 8, i64 20, i64 24, i64 0, i64 0, i64 0), "gc-live"(i8 addrspace(1)* [[OBJ_X_BASE1]]) ]
+; CHECK-NEXT:    [[OBJ_X_BASE1_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
+; CHECK-NEXT:    ret i8 addrspace(1)* [[OBJ_X_BASE1_RELOCATED]]
+;
+entry:
+  %obj1.12 = getelementptr inbounds i8, i8 addrspace(1)* %obj1, i64 12
+  %obj2.16 = getelementptr inbounds i8, i8 addrspace(1)* %obj2, i64 16
+  %obj.x = select i1 %c, i8 addrspace(1)* %obj1.12, i8 addrspace(1)* %obj2.16
+
+  %obj.x.base = call i8 addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1i8.p1i8(i8 addrspace(1)* %obj.x)
+  %obj.x.base_of_base = call i8 addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1i8.p1i8(i8 addrspace(1)* %obj.x.base)
+  %obj.x.base_of_base_of_base = call i8 addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1i8.p1i8(i8 addrspace(1)* %obj.x.base_of_base)
+
+  %obj.x.base_gep                 = getelementptr inbounds i8, i8 addrspace(1)* %obj.x.base, i64 8
+  %obj.x.base_of_base_gep         = getelementptr inbounds i8, i8 addrspace(1)* %obj.x.base_of_base, i64 20
+  %obj.x.base_of_base_of_base_gep = getelementptr inbounds i8, i8 addrspace(1)* %obj.x.base_of_base_of_base, i64 24
+
+  %obj.x.base_gep_base                 = call i8 addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1i8.p1i8(i8 addrspace(1)* %obj.x.base_gep)
+  %obj.x.base_of_base_gep_base         = call i8 addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1i8.p1i8(i8 addrspace(1)* %obj.x.base_of_base_gep)
+  %obj.x.base_of_base_of_base_gep_base = call i8 addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1i8.p1i8(i8 addrspace(1)* %obj.x.base_of_base_of_base_gep)
+
+  %obj.x.base_offset                          = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x.base)
+  %obj.x.base_of_base_offset                  = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x.base_of_base)
+  %obj.x.base_of_base_of_base_offset          = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x.base_of_base_of_base)
+  %obj.x.base_gep_offset                      = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x.base_gep)
+  %obj.x.base_of_base_gep_offset              = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x.base_of_base_gep)
+  %obj.x.base_of_base_of_base_gep_offset      = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x.base_of_base_of_base_gep)
+  %obj.x.base_gep_base_offset                 = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x.base_gep_base)
+  %obj.x.base_of_base_gep_base_offset         = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x.base_of_base_gep_base)
+  %obj.x.base_of_base_of_base_gep_base_offset = call i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* %obj.x.base_of_base_of_base_gep_base)
+
+  call void @foo() readonly [
+  "deopt"(
+      i8 addrspace(1)* %obj.x.base,
+      i8 addrspace(1)* %obj.x.base_of_base_of_base,
+      i8 addrspace(1)* %obj.x.base_of_base,
+      i8 addrspace(1)* %obj.x.base_gep,
+      i8 addrspace(1)* %obj.x.base_of_base_gep,
+      i8 addrspace(1)* %obj.x.base_of_base_of_base_gep,
+      i8 addrspace(1)* %obj.x.base_gep_base,
+      i8 addrspace(1)* %obj.x.base_of_base_gep_base,
+      i8 addrspace(1)* %obj.x.base_of_base_of_base_gep_base,
+      i64 %obj.x.base_offset,
+      i64 %obj.x.base_of_base_offset,
+      i64 %obj.x.base_of_base_of_base_offset,
+      i64 %obj.x.base_gep_offset,
+      i64 %obj.x.base_of_base_gep_offset,
+      i64 %obj.x.base_of_base_of_base_gep_offset,
+      i64 %obj.x.base_gep_base_offset,
+      i64 %obj.x.base_of_base_gep_base_offset,
+      i64 %obj.x.base_of_base_of_base_gep_base_offset) ]
+
+  ret i8 addrspace(1)* %obj.x.base_of_base
+}
Index: llvm/utils/TableGen/CodeGenIntrinsics.h
===================================================================
--- llvm/utils/TableGen/CodeGenIntrinsics.h
+++ llvm/utils/TableGen/CodeGenIntrinsics.h
@@ -117,6 +117,9 @@
   /// True if the intrinsic can throw.
   bool canThrow;
 
+  /// True if the intrinsic has attribute "gc-leaf-function".
+  bool isGCLeafFunction;
+
   /// True if the intrinsic is marked as noduplicate.
   bool isNoDuplicate;
 
Index: llvm/utils/TableGen/CodeGenTarget.cpp
===================================================================
--- llvm/utils/TableGen/CodeGenTarget.cpp
+++ llvm/utils/TableGen/CodeGenTarget.cpp
@@ -650,6 +650,7 @@
   isOverloaded = false;
   isCommutative = false;
   canThrow = false;
+  isGCLeafFunction = false;
   isNoReturn = false;
   isNoSync = false;
   isNoFree = false;
@@ -844,6 +845,8 @@
     isCommutative = true;
   else if (R->getName() == "Throws")
     canThrow = true;
+  else if (R->getName() == "IntrGCLeafFunction")
+    isGCLeafFunction = true;
   else if (R->getName() == "IntrNoDuplicate")
     isNoDuplicate = true;
   else if (R->getName() == "IntrNoMerge")
Index: llvm/utils/TableGen/IntrinsicEmitter.cpp
===================================================================
--- llvm/utils/TableGen/IntrinsicEmitter.cpp
+++ llvm/utils/TableGen/IntrinsicEmitter.cpp
@@ -581,6 +581,9 @@
     if (L->canThrow != R->canThrow)
       return R->canThrow;
 
+    if (L->isGCLeafFunction != R->isGCLeafFunction)
+      return R->isGCLeafFunction;
+
     if (L->isNoDuplicate != R->isNoDuplicate)
       return R->isNoDuplicate;
 
@@ -737,6 +740,7 @@
       }
     }
 
+    bool isThereFuncAttr = false;
     if (!intrinsic.canThrow ||
         (intrinsic.ModRef != CodeGenIntrinsic::ReadWriteMem &&
          !intrinsic.hasSideEffects) ||
@@ -830,6 +834,13 @@
       OS << "};\n";
       OS << "      AS[" << numAttrs++ << "] = AttributeList::get(C, "
          << "AttributeList::FunctionIndex, Atts);\n";
+      isThereFuncAttr = true;
+    }
+    if (intrinsic.isGCLeafFunction) {
+      unsigned FuncIndex = isThereFuncAttr ? numAttrs - 1 : numAttrs++;
+      OS << "      AS[" << FuncIndex << "] = AS[" << FuncIndex
+         << "].addAttribute(C, "
+         << "AttributeList::FunctionIndex, \"gc-leaf-function\");\n";
     }
 
     if (numAttrs) {