[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