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) {