[llvm] 4d26f41 - [RS4GC] Introduce intrinsics to get base ptr and offset

Yevgeny Rouban via llvm-commits llvm-commits at lists.llvm.org
Wed May 26 19:15:49 PDT 2021


Author: Yevgeny Rouban
Date: 2021-05-27T09:14:14+07:00
New Revision: 4d26f41f76c4f92023c02ec96ffbd02a6eb2c46d

URL: https://github.com/llvm/llvm-project/commit/4d26f41f76c4f92023c02ec96ffbd02a6eb2c46d
DIFF: https://github.com/llvm/llvm-project/commit/4d26f41f76c4f92023c02ec96ffbd02a6eb2c46d.diff

LOG: [RS4GC] Introduce intrinsics to get base ptr and offset

There can be a need for some optimizations to get (base, offset)
for any GC pointer. The base can be calculated by generating
needed instructions as it is done by the
RewriteStatepointsForGC::findBasePointer() function. The offset
can be calculated in the same way. Though to not expose the base
calculation and to make the offset calculation as simple as
ptrtoint(derived_ptr) - ptrtoint(base_ptr), which is illegal
outside RS4GC, this patch introduces 2 intrinsics:

 @llvm.experimental.gc.get.pointer.base(%derived_ptr)
 @llvm.experimental.gc.get.pointer.offset(%derived_ptr)

These intrinsics are inlined by RS4GC along with generation of
statepoint sequences.

With these new intrinsics the GC parseable lowering for atomic
memcpy intrinsics (6ec2c5e402a724ba99bce82a9cac7a3006d660f4)
could be implemented as a separate pass.

Reviewed By: reames
Differential Revision: https://reviews.llvm.org/D100445

Added: 
    llvm/test/Transforms/RewriteStatepointsForGC/intrinsics.ll

Modified: 
    llvm/docs/LangRef.rst
    llvm/docs/Statepoints.rst
    llvm/include/llvm/IR/IRBuilder.h
    llvm/include/llvm/IR/Intrinsics.td
    llvm/lib/IR/IRBuilder.cpp
    llvm/lib/IR/Verifier.cpp
    llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 1ee3bd847d2c..d6bfafaccb61 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -12250,6 +12250,88 @@ A ``gc.relocate`` is modeled as a ``readnone`` pure function.  It has no
 side effects since it is just a way to extract information about work
 done during the actual call modeled by the ``gc.statepoint``.
 
+.. _gc.get.pointer.base:
+
+'llvm.experimental.gc.get.pointer.base' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+      declare <pointer type>
+        @llvm.experimental.gc.get.pointer.base(
+          <pointer type> readnone nocapture %derived_ptr)
+          nounwind readnone willreturn
+
+Overview:
+"""""""""
+
+``gc.get.pointer.base`` for a derived pointer returns its base pointer.
+
+Operands:
+"""""""""
+
+The only argument is a pointer which is based on some object with
+an unknown offset from the base of said object.
+
+Semantics:
+""""""""""
+
+This intrinsic is used in the abstract machine model for GC to represent
+the base pointer for an arbitrary derived pointer.
+
+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.
+
+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
+
+Overview:
+"""""""""
+
+``gc.get.pointer.offset`` for a derived pointer returns the offset from its
+base pointer.
+
+Operands:
+"""""""""
+
+The only argument is a pointer which is based on some object with
+an unknown offset from the base of said object.
+
+Semantics:
+""""""""""
+
+This intrinsic is used in the abstract machine model for GC to represent
+the offset of an arbitrary derived pointer from its base pointer.
+
+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 
diff erence 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.
+
 Code Generator Intrinsics
 -------------------------
 

diff  --git a/llvm/docs/Statepoints.rst b/llvm/docs/Statepoints.rst
index 765384bfefc3..e13f5d9e4add 100644
--- a/llvm/docs/Statepoints.rst
+++ b/llvm/docs/Statepoints.rst
@@ -430,6 +430,13 @@ strategy-specific lowering is not present, and all GC transitions are emitted as
 as single no-op before and after the call instruction. These no-ops are often
 removed by the backend during dead machine instruction elimination.
 
+Before the abstract machine model is lowered to the explicit statepoint model
+of relocations by the :ref:`RewriteStatepointsForGC` pass it is possible for
+any derived pointer to get its base pointer and offset from the base pointer
+by using the ``gc.get.pointer.base`` and the ``gc.get.pointer.offset``
+intrinsics respectively. These intrinsics are inlined by the
+:ref:`RewriteStatepointsForGC` pass and must not be used after this pass.
+
 
 .. _statepoint-stackmap-format:
 
@@ -620,12 +627,16 @@ RewriteStatepointsForGC intrinsic lowering
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 As a part of lowering to the explicit model of relocations
-RewriteStatepointsForGC performs GC specific lowering for
-'``llvm.memcpy.element.unordered.atomic.*``',
-'``llvm.memmove.element.unordered.atomic.*``' intrinsics.
+RewriteStatepointsForGC performs GC specific lowering for the following
+intrinsics:
+
+* ``gc.get.pointer.base``
+* ``gc.get.pointer.offset``
+* ``llvm.memcpy.element.unordered.atomic.*``
+* ``llvm.memmove.element.unordered.atomic.*``
 
-There are two possible lowerings for these copy operations: GC leaf lowering
-and GC parseable lowering. If a call is explicitly marked with
+There are two possible lowerings for the memcpy and memmove operations:
+GC leaf lowering and GC parseable lowering. If a call is explicitly marked with
 "gc-leaf-function" attribute the call is lowered to a GC leaf call to
 '``__llvm_memcpy_element_unordered_atomic_*``' or
 '``__llvm_memmove_element_unordered_atomic_*``' symbol. Such a call can not

diff  --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h
index fcf6481f6a6b..7fb504ad0d66 100644
--- a/llvm/include/llvm/IR/IRBuilder.h
+++ b/llvm/include/llvm/IR/IRBuilder.h
@@ -850,6 +850,14 @@ class IRBuilderBase {
                              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 = "");

diff  --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 215188e2f182..f7c0bb0b88a9 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1206,6 +1206,14 @@ def int_experimental_gc_relocate : Intrinsic<[llvm_any_ty],
                                              [IntrNoMem, ImmArg<ArgIndex<1>>,
                                               ImmArg<ArgIndex<2>>]>;
 
+def int_experimental_gc_get_pointer_base : Intrinsic<[llvm_anyptr_ty],
+                 [llvm_anyptr_ty], [IntrNoMem, IntrWillReturn,
+                 ReadNone<ArgIndex<0>>, NoCapture<ArgIndex<0>>]>;
+
+def int_experimental_gc_get_pointer_offset : Intrinsic<[llvm_i64_ty],
+                 [llvm_anyptr_ty], [IntrNoMem, IntrWillReturn,
+                 ReadNone<ArgIndex<0>>, NoCapture<ArgIndex<0>>]>;
+
 //===------------------------ Coroutine Intrinsics ---------------===//
 // These are documented in docs/Coroutines.rst
 

diff  --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp
index 2724be831ee1..ca6577330c8e 100644
--- a/llvm/lib/IR/IRBuilder.cpp
+++ b/llvm/lib/IR/IRBuilder.cpp
@@ -780,6 +780,24 @@ CallInst *IRBuilderBase::CreateGCRelocate(Instruction *Statepoint,
  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) {

diff  --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 22d9e7717a9f..bcb88c6a2cd2 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2617,6 +2617,29 @@ void Verifier::visitFunction(const Function &F) {
       Assert(false, "Invalid user of intrinsic instruction!", U);
   }
 
+  // Check intrinsics' signatures.
+  switch (F.getIntrinsicID()) {
+  case Intrinsic::experimental_gc_get_pointer_base: {
+    FunctionType *FT = F.getFunctionType();
+    Assert(FT->getNumParams() == 1, "wrong number of parameters", F);
+    Assert(isa<PointerType>(F.getReturnType()),
+           "gc.get.pointer.base must return a pointer", F);
+    Assert(FT->getParamType(0) == F.getReturnType(),
+           "gc.get.pointer.base operand and result must be of the same type",
+           F);
+    break;
+  }
+  case Intrinsic::experimental_gc_get_pointer_offset: {
+    FunctionType *FT = F.getFunctionType();
+    Assert(FT->getNumParams() == 1, "wrong number of parameters", F);
+    Assert(isa<PointerType>(FT->getParamType(0)),
+           "gc.get.pointer.offset operand must be a pointer", F);
+    Assert(F.getReturnType()->isIntegerTy(),
+           "gc.get.pointer.offset must return integer", F);
+    break;
+  }
+  }
+
   auto *N = F.getSubprogram();
   HasDebugInfo = (N != nullptr);
   if (!HasDebugInfo)

diff  --git a/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp b/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
index 051c6f976e52..6590859df107 100644
--- a/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
+++ b/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
@@ -562,6 +562,8 @@ static BaseDefiningValueResult findBaseDefiningValue(Value *I) {
       // 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 @@ static BaseDefiningValueResult findBaseDefiningValue(Value *I) {
   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 @@ static BaseDefiningValueResult findBaseDefiningValue(Value *I) {
     // 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 @@ static BaseDefiningValueResult findBaseDefiningValue(Value *I) {
   // 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.
@@ -2384,6 +2391,56 @@ static void rematerializeLiveValues(CallBase *Call,
   }
 }
 
+static bool inlineGetBaseAndOffset(Function &F,
+                                   SmallVectorImpl<CallInst *> &Intrinsics) {
+  DefiningValueMapTy DVCache;
+  auto &Context = F.getContext();
+  auto &DL = F.getParent()->getDataLayout();
+  bool Changed = false;
+
+  for (auto *Callsite : Intrinsics)
+    switch (Callsite->getIntrinsicID()) {
+    case 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();
+      break;
+    }
+    case 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();
+      break;
+    }
+    default:
+      llvm_unreachable("Unknown intrinsic");
+    }
+
+  return Changed;
+}
+
 static bool insertParsePoints(Function &F, DominatorTree &DT,
                               TargetTransformInfo &TTI,
                               SmallVectorImpl<CallBase *> &ToUpdate) {
@@ -2442,7 +2499,6 @@ static bool insertParsePoints(Function &F, DominatorTree &DT,
     // 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);
@@ -2785,6 +2841,7 @@ bool RewriteStatepointsForGC::runOnFunction(Function &F, DominatorTree &DT,
   // 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)) {
@@ -2796,10 +2853,14 @@ bool RewriteStatepointsForGC::runOnFunction(Function &F, DominatorTree &DT,
             "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.
@@ -2868,7 +2929,13 @@ bool RewriteStatepointsForGC::runOnFunction(Function &F, DominatorTree &DT,
     }
   }
 
-  MadeChange |= insertParsePoints(F, DT, TTI, ParsePointNeeded);
+  if (!Intrinsics.empty())
+    // Inline @gc.get.pointer.base() and @gc.get.pointer.offset() before finding
+    // live references.
+    MadeChange |= inlineGetBaseAndOffset(F, Intrinsics);
+
+  if (!ParsePointNeeded.empty())
+    MadeChange |= insertParsePoints(F, DT, TTI, ParsePointNeeded);
   return MadeChange;
 }
 

diff  --git a/llvm/test/Transforms/RewriteStatepointsForGC/intrinsics.ll b/llvm/test/Transforms/RewriteStatepointsForGC/intrinsics.ll
new file mode 100644
index 000000000000..8344f3c44e48
--- /dev/null
+++ b/llvm/test/Transforms/RewriteStatepointsForGC/intrinsics.ll
@@ -0,0 +1,134 @@
+; 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
+declare i8 addrspace(1)* addrspace(1)* @llvm.experimental.gc.get.pointer.base.p1p1i8.p1p1i8(i8 addrspace(1)* addrspace(1)* readnone nocapture) nounwind readnone willreturn
+declare i64 @llvm.experimental.gc.get.pointer.offset.p1i8(i8 addrspace(1)* readnone nocapture) nounwind readnone willreturn
+declare i64 @llvm.experimental.gc.get.pointer.offset.p1p1i8(i8 addrspace(1)* addrspace(1)* readnone nocapture) nounwind readnone willreturn
+
+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]]
+; 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:    [[OBJ_X_BASE1_INT:%.*]] = ptrtoint i8 addrspace(1)* [[OBJ_X_BASE1]] to i64
+; CHECK-NEXT:    [[OBJ_X_INT:%.*]] = ptrtoint i8 addrspace(1)* [[OBJ_X]] to i64
+; CHECK-NEXT:    [[OBJ_X_OFFSET:%.*]] = sub i64 [[OBJ_X_INT]], [[OBJ_X_BASE1_INT]]
+; CHECK-NEXT:    [[OBJ_Y_BASE_CAST:%.*]] = bitcast i8 addrspace(1)* [[OBJ_Y_BASE]] to i8 addrspace(1)* addrspace(1)*
+; CHECK-NEXT:    [[OBJ_Y_BASE_INT:%.*]] = ptrtoint i8 addrspace(1)* [[OBJ_Y_BASE]] to i64
+; CHECK-NEXT:    [[OBJ_YA_INT:%.*]] = ptrtoint i8 addrspace(1)* [[OBJ_Y]] to i64
+; CHECK-NEXT:    [[OBJ_YA_OFFSET:%.*]] = sub i64 [[OBJ_YA_INT]], [[OBJ_Y_BASE_INT]]
+; 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_Y_BASE_CAST]], 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 void @test_base_of_base(i8 addrspace(1)* %obj1, i8 addrspace(1)* %obj2, i32 %len, i1 %c) gc "statepoint-example" {
+; CHECK-LABEL: define {{[^@]+}}@test_base_of_base
+; CHECK-SAME: (i8 addrspace(1)* [[OBJ1:%.*]], i8 addrspace(1)* [[OBJ2:%.*]], i32 [[LEN:%.*]], i1 [[C:%.*]]) gc "statepoint-example" {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret void
+;
+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)
+
+  ret void
+}
+
+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
+}


        


More information about the llvm-commits mailing list