forked from OSchip/llvm-project
[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 (6ec2c5e402
)
could be implemented as a separate pass.
Reviewed By: reames
Differential Revision: https://reviews.llvm.org/D100445
This commit is contained in:
parent
a4b61c82cf
commit
4d26f41f76
|
@ -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 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
|
||||
-------------------------
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
There are two possible lowerings for these copy operations: GC leaf lowering
|
||||
and GC parseable lowering. If a call is explicitly marked with
|
||||
* ``gc.get.pointer.base``
|
||||
* ``gc.get.pointer.offset``
|
||||
* ``llvm.memcpy.element.unordered.atomic.*``
|
||||
* ``llvm.memmove.element.unordered.atomic.*``
|
||||
|
||||
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
|
||||
|
|
|
@ -850,6 +850,14 @@ public:
|
|||
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 = "");
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue