diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -12250,6 +12250,88 @@ 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 + @llvm.experimental.gc.get.pointer.base( + 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( + 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 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. + Code Generator Intrinsics ------------------------- diff --git a/llvm/docs/Statepoints.rst b/llvm/docs/Statepoints.rst --- a/llvm/docs/Statepoints.rst +++ b/llvm/docs/Statepoints.rst @@ -430,6 +430,13 @@ 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 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/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 = ""); diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1206,6 +1206,14 @@ [IntrNoMem, ImmArg>, ImmArg>]>; +def int_experimental_gc_get_pointer_base : Intrinsic<[llvm_anyptr_ty], + [llvm_anyptr_ty], [IntrNoMem, IntrWillReturn, + ReadNone>, NoCapture>]>; + +def int_experimental_gc_get_pointer_offset : Intrinsic<[llvm_i64_ty], + [llvm_anyptr_ty], [IntrNoMem, IntrWillReturn, + ReadNone>, NoCapture>]>; + //===------------------------ Coroutine Intrinsics ---------------===// // These are documented in docs/Coroutines.rst diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp --- a/llvm/lib/IR/IRBuilder.cpp +++ b/llvm/lib/IR/IRBuilder.cpp @@ -780,6 +780,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) { diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2617,6 +2617,29 @@ 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(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(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 --- a/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp +++ b/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(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(I) && cast(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(I) || isa(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 bool inlineGetBaseAndOffset(Function &F, + SmallVectorImpl &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 &ToUpdate) { @@ -2442,7 +2499,6 @@ // 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 @@ // consider those in reachable code since we need to ask dominance queries // when rewriting. We'll delete the unreachable ones in a moment. SmallVector ParsePointNeeded; + SmallVector Intrinsics; for (Instruction &I : instructions(F)) { // TODO: only the ones with the flag set! if (NeedsRewrite(I)) { @@ -2796,10 +2853,14 @@ "no unreachable blocks expected"); ParsePointNeeded.push_back(cast(&I)); } + if (auto *CI = dyn_cast(&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 @@ } } - 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 --- /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 +}