[InstCombine] Move statepoint intrinsic handling from visitCall to visitCallBase

statepoint intrinsic can be used in invoke context,
so it should be handled in visitCallBase to cover both call and invoke.

Reviewers: reames, dantrushin
Reviewed By: reames
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D97833
This commit is contained in:
Serguei Katkov 2021-03-03 12:41:00 +07:00
parent e7e67c930a
commit a0ff0f30df
3 changed files with 405 additions and 104 deletions

View File

@ -1672,99 +1672,6 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
AC.updateAffectedValues(II);
break;
}
case Intrinsic::experimental_gc_statepoint: {
GCStatepointInst &GCSP = *cast<GCStatepointInst>(II);
SmallPtrSet<Value *, 32> LiveGcValues;
for (const GCRelocateInst *Reloc : GCSP.getGCRelocates()) {
GCRelocateInst &GCR = *const_cast<GCRelocateInst *>(Reloc);
// Remove the relocation if unused.
if (GCR.use_empty()) {
eraseInstFromFunction(GCR);
continue;
}
Value *DerivedPtr = GCR.getDerivedPtr();
Value *BasePtr = GCR.getBasePtr();
// Undef is undef, even after relocation.
if (isa<UndefValue>(DerivedPtr) || isa<UndefValue>(BasePtr)) {
replaceInstUsesWith(GCR, UndefValue::get(GCR.getType()));
eraseInstFromFunction(GCR);
continue;
}
if (auto *PT = dyn_cast<PointerType>(GCR.getType())) {
// The relocation of null will be null for most any collector.
// TODO: provide a hook for this in GCStrategy. There might be some
// weird collector this property does not hold for.
if (isa<ConstantPointerNull>(DerivedPtr)) {
// Use null-pointer of gc_relocate's type to replace it.
replaceInstUsesWith(GCR, ConstantPointerNull::get(PT));
eraseInstFromFunction(GCR);
continue;
}
// isKnownNonNull -> nonnull attribute
if (!GCR.hasRetAttr(Attribute::NonNull) &&
isKnownNonZero(DerivedPtr, DL, 0, &AC, II, &DT)) {
GCR.addAttribute(AttributeList::ReturnIndex, Attribute::NonNull);
// We discovered new fact, re-check users.
Worklist.pushUsersToWorkList(GCR);
}
}
// If we have two copies of the same pointer in the statepoint argument
// list, canonicalize to one. This may let us common gc.relocates.
if (GCR.getBasePtr() == GCR.getDerivedPtr() &&
GCR.getBasePtrIndex() != GCR.getDerivedPtrIndex()) {
auto *OpIntTy = GCR.getOperand(2)->getType();
GCR.setOperand(2, ConstantInt::get(OpIntTy, GCR.getBasePtrIndex()));
}
// TODO: bitcast(relocate(p)) -> relocate(bitcast(p))
// Canonicalize on the type from the uses to the defs
// TODO: relocate((gep p, C, C2, ...)) -> gep(relocate(p), C, C2, ...)
LiveGcValues.insert(BasePtr);
LiveGcValues.insert(DerivedPtr);
}
Optional<OperandBundleUse> Bundle =
GCSP.getOperandBundle(LLVMContext::OB_gc_live);
unsigned NumOfGCLives = LiveGcValues.size();
if (!Bundle.hasValue() || NumOfGCLives == Bundle->Inputs.size())
break;
// We can reduce the size of gc live bundle.
DenseMap<Value *, unsigned> Val2Idx;
std::vector<Value *> NewLiveGc;
for (unsigned I = 0, E = Bundle->Inputs.size(); I < E; ++I) {
Value *V = Bundle->Inputs[I];
if (Val2Idx.count(V))
continue;
if (LiveGcValues.count(V)) {
Val2Idx[V] = NewLiveGc.size();
NewLiveGc.push_back(V);
} else
Val2Idx[V] = NumOfGCLives;
}
// Update all gc.relocates
for (const GCRelocateInst *Reloc : GCSP.getGCRelocates()) {
GCRelocateInst &GCR = *const_cast<GCRelocateInst *>(Reloc);
Value *BasePtr = GCR.getBasePtr();
assert(Val2Idx.count(BasePtr) && Val2Idx[BasePtr] != NumOfGCLives &&
"Missed live gc for base pointer");
auto *OpIntTy1 = GCR.getOperand(1)->getType();
GCR.setOperand(1, ConstantInt::get(OpIntTy1, Val2Idx[BasePtr]));
Value *DerivedPtr = GCR.getDerivedPtr();
assert(Val2Idx.count(DerivedPtr) && Val2Idx[DerivedPtr] != NumOfGCLives &&
"Missed live gc for derived pointer");
auto *OpIntTy2 = GCR.getOperand(2)->getType();
GCR.setOperand(2, ConstantInt::get(OpIntTy2, Val2Idx[DerivedPtr]));
}
// Create new statepoint instruction.
OperandBundleDef NewBundle("gc-live", NewLiveGc);
return CallBase::Create(II, NewBundle);
}
case Intrinsic::experimental_guard: {
// Is this guard followed by another guard? We scan forward over a small
// fixed window of instructions to handle common cases with conditions
@ -1900,6 +1807,8 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
break;
}
}
// Some intrinsics (like experimental_gc_statepoint) can be used in invoke
// context, so it is handled in visitCallBase and we should trigger it.
return visitCallBase(*II);
}
@ -2262,6 +2171,104 @@ Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) {
if (isAllocLikeFn(&Call, &TLI))
return visitAllocSite(Call);
// Handle intrinsics which can be used in both call and invoke context.
switch (Call.getIntrinsicID()) {
case Intrinsic::experimental_gc_statepoint: {
GCStatepointInst &GCSP = *cast<GCStatepointInst>(&Call);
SmallPtrSet<Value *, 32> LiveGcValues;
for (const GCRelocateInst *Reloc : GCSP.getGCRelocates()) {
GCRelocateInst &GCR = *const_cast<GCRelocateInst *>(Reloc);
// Remove the relocation if unused.
if (GCR.use_empty()) {
eraseInstFromFunction(GCR);
continue;
}
Value *DerivedPtr = GCR.getDerivedPtr();
Value *BasePtr = GCR.getBasePtr();
// Undef is undef, even after relocation.
if (isa<UndefValue>(DerivedPtr) || isa<UndefValue>(BasePtr)) {
replaceInstUsesWith(GCR, UndefValue::get(GCR.getType()));
eraseInstFromFunction(GCR);
continue;
}
if (auto *PT = dyn_cast<PointerType>(GCR.getType())) {
// The relocation of null will be null for most any collector.
// TODO: provide a hook for this in GCStrategy. There might be some
// weird collector this property does not hold for.
if (isa<ConstantPointerNull>(DerivedPtr)) {
// Use null-pointer of gc_relocate's type to replace it.
replaceInstUsesWith(GCR, ConstantPointerNull::get(PT));
eraseInstFromFunction(GCR);
continue;
}
// isKnownNonNull -> nonnull attribute
if (!GCR.hasRetAttr(Attribute::NonNull) &&
isKnownNonZero(DerivedPtr, DL, 0, &AC, &Call, &DT)) {
GCR.addAttribute(AttributeList::ReturnIndex, Attribute::NonNull);
// We discovered new fact, re-check users.
Worklist.pushUsersToWorkList(GCR);
}
}
// If we have two copies of the same pointer in the statepoint argument
// list, canonicalize to one. This may let us common gc.relocates.
if (GCR.getBasePtr() == GCR.getDerivedPtr() &&
GCR.getBasePtrIndex() != GCR.getDerivedPtrIndex()) {
auto *OpIntTy = GCR.getOperand(2)->getType();
GCR.setOperand(2, ConstantInt::get(OpIntTy, GCR.getBasePtrIndex()));
}
// TODO: bitcast(relocate(p)) -> relocate(bitcast(p))
// Canonicalize on the type from the uses to the defs
// TODO: relocate((gep p, C, C2, ...)) -> gep(relocate(p), C, C2, ...)
LiveGcValues.insert(BasePtr);
LiveGcValues.insert(DerivedPtr);
}
Optional<OperandBundleUse> Bundle =
GCSP.getOperandBundle(LLVMContext::OB_gc_live);
unsigned NumOfGCLives = LiveGcValues.size();
if (!Bundle.hasValue() || NumOfGCLives == Bundle->Inputs.size())
break;
// We can reduce the size of gc live bundle.
DenseMap<Value *, unsigned> Val2Idx;
std::vector<Value *> NewLiveGc;
for (unsigned I = 0, E = Bundle->Inputs.size(); I < E; ++I) {
Value *V = Bundle->Inputs[I];
if (Val2Idx.count(V))
continue;
if (LiveGcValues.count(V)) {
Val2Idx[V] = NewLiveGc.size();
NewLiveGc.push_back(V);
} else
Val2Idx[V] = NumOfGCLives;
}
// Update all gc.relocates
for (const GCRelocateInst *Reloc : GCSP.getGCRelocates()) {
GCRelocateInst &GCR = *const_cast<GCRelocateInst *>(Reloc);
Value *BasePtr = GCR.getBasePtr();
assert(Val2Idx.count(BasePtr) && Val2Idx[BasePtr] != NumOfGCLives &&
"Missed live gc for base pointer");
auto *OpIntTy1 = GCR.getOperand(1)->getType();
GCR.setOperand(1, ConstantInt::get(OpIntTy1, Val2Idx[BasePtr]));
Value *DerivedPtr = GCR.getDerivedPtr();
assert(Val2Idx.count(DerivedPtr) && Val2Idx[DerivedPtr] != NumOfGCLives &&
"Missed live gc for derived pointer");
auto *OpIntTy2 = GCR.getOperand(2)->getType();
GCR.setOperand(2, ConstantInt::get(OpIntTy2, Val2Idx[DerivedPtr]));
}
// Create new statepoint instruction.
OperandBundleDef NewBundle("gc-live", NewLiveGc);
return CallBase::Create(&Call, NewBundle);
}
default: { break; }
}
return Changed ? &Call : nullptr;
}

View File

@ -4,6 +4,7 @@
; pointers being relocated at a statepoint.
declare i32* @fake_personality_function()
declare void @func()
define void @test(i32 addrspace(1)* %b) gc "statepoint-example" {
@ -86,5 +87,169 @@ entry:
ret void
}
define void @test_invoke(i32 addrspace(1)* %b) gc "statepoint-example" personality i32* ()* @fake_personality_function {
; CHECK-LABEL: @test_invoke(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[D:%.*]] = getelementptr i32, i32 addrspace(1)* [[B:%.*]], i64 16
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"(i32 addrspace(1)* [[B]], i32 addrspace(1)* [[D]]) ]
; CHECK-NEXT: to label [[NORMAL_DEST:%.*]] unwind label [[UNWIND_DEST:%.*]]
; CHECK: normal_dest:
; CHECK-NEXT: [[B_NEW_1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: [[B_NEW_2:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: [[D_NEW_1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 1)
; CHECK-NEXT: [[D_NEW_2:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 1)
; CHECK-NEXT: [[D_NEW_3:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 1)
; CHECK-NEXT: [[D_NEW_4:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 1)
; CHECK-NEXT: store i32 1, i32 addrspace(1)* [[B_NEW_1]], align 4
; CHECK-NEXT: store i32 1, i32 addrspace(1)* [[B_NEW_2]], align 4
; CHECK-NEXT: store i32 1, i32 addrspace(1)* [[D_NEW_1]], align 4
; CHECK-NEXT: store i32 1, i32 addrspace(1)* [[D_NEW_2]], align 4
; CHECK-NEXT: store i32 1, i32 addrspace(1)* [[D_NEW_3]], align 4
; CHECK-NEXT: store i32 1, i32 addrspace(1)* [[D_NEW_4]], align 4
; CHECK-NEXT: ret void
; CHECK: unwind_dest:
; CHECK-NEXT: [[LPAD:%.*]] = landingpad token
; CHECK-NEXT: cleanup
; CHECK-NEXT: [[LPB_NEW_1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LPAD]], i32 0, i32 0)
; CHECK-NEXT: [[LPB_NEW_2:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LPAD]], i32 0, i32 0)
; CHECK-NEXT: [[LPD_NEW_1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LPAD]], i32 0, i32 1)
; CHECK-NEXT: [[LPD_NEW_2:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LPAD]], i32 0, i32 1)
; CHECK-NEXT: [[LPD_NEW_3:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LPAD]], i32 0, i32 1)
; CHECK-NEXT: [[LPD_NEW_4:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LPAD]], i32 0, i32 1)
; CHECK-NEXT: store i32 2, i32 addrspace(1)* [[LPB_NEW_1]], align 4
; CHECK-NEXT: store i32 2, i32 addrspace(1)* [[LPB_NEW_2]], align 4
; CHECK-NEXT: store i32 2, i32 addrspace(1)* [[LPD_NEW_1]], align 4
; CHECK-NEXT: store i32 2, i32 addrspace(1)* [[LPD_NEW_2]], align 4
; CHECK-NEXT: store i32 2, i32 addrspace(1)* [[LPD_NEW_3]], align 4
; CHECK-NEXT: store i32 2, i32 addrspace(1)* [[LPD_NEW_4]], align 4
; CHECK-NEXT: ret void
;
entry:
%d = getelementptr i32, i32 addrspace(1)* %b, i64 16
%safepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live" (i32 addrspace(1)* %b, i32 addrspace(1)* %b, i32 addrspace(1)* %d, i32 addrspace(1)* %d)]
to label %normal_dest unwind label %unwind_dest
normal_dest:
%b.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%b.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 1)
%d.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 2)
%d.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 3)
%d.new.3 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 1, i32 2)
%d.new.4 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 1, i32 3)
store i32 1, i32 addrspace(1)* %b.new.1
store i32 1, i32 addrspace(1)* %b.new.2
store i32 1, i32 addrspace(1)* %d.new.1
store i32 1, i32 addrspace(1)* %d.new.2
store i32 1, i32 addrspace(1)* %d.new.3
store i32 1, i32 addrspace(1)* %d.new.4
ret void
unwind_dest:
%lpad = landingpad token
cleanup
%lpb.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 0)
%lpb.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 1)
%lpd.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 2)
%lpd.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 3)
%lpd.new.3 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 1, i32 2)
%lpd.new.4 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 1, i32 3)
store i32 2, i32 addrspace(1)* %lpb.new.1
store i32 2, i32 addrspace(1)* %lpb.new.2
store i32 2, i32 addrspace(1)* %lpd.new.1
store i32 2, i32 addrspace(1)* %lpd.new.2
store i32 2, i32 addrspace(1)* %lpd.new.3
store i32 2, i32 addrspace(1)* %lpd.new.4
ret void
}
define void @test_no_derived_use_invoke(i32 addrspace(1)* %b) gc "statepoint-example" personality i32* ()* @fake_personality_function {
; CHECK-LABEL: @test_no_derived_use_invoke(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"(i32 addrspace(1)* [[B:%.*]]) ]
; CHECK-NEXT: to label [[NORMAL_DEST:%.*]] unwind label [[UNWIND_DEST:%.*]]
; CHECK: normal_dest:
; CHECK-NEXT: [[B_NEW_1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: store i32 1, i32 addrspace(1)* [[B_NEW_1]], align 4
; CHECK-NEXT: ret void
; CHECK: unwind_dest:
; CHECK-NEXT: [[LPAD:%.*]] = landingpad token
; CHECK-NEXT: cleanup
; CHECK-NEXT: [[LPB_NEW_1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LPAD]], i32 0, i32 0)
; CHECK-NEXT: store i32 2, i32 addrspace(1)* [[LPB_NEW_1]], align 4
; CHECK-NEXT: ret void
;
entry:
%d = getelementptr i32, i32 addrspace(1)* %b, i64 16
%safepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live" (i32 addrspace(1)* %b, i32 addrspace(1)* %b, i32 addrspace(1)* %d, i32 addrspace(1)* %d)]
to label %normal_dest unwind label %unwind_dest
normal_dest:
%b.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%b.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 1)
%d.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 2)
%d.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 3)
%d.new.3 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 1, i32 2)
%d.new.4 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 1, i32 3)
store i32 1, i32 addrspace(1)* %b.new.1
ret void
unwind_dest:
%lpad = landingpad token
cleanup
%lpb.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 0)
%lpb.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 1)
%lpd.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 2)
%lpd.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 3)
%lpd.new.3 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 1, i32 2)
%lpd.new.4 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 1, i32 3)
store i32 2, i32 addrspace(1)* %lpb.new.1
ret void
}
define void @test_no_base_use_invoke(i32 addrspace(1)* %b) gc "statepoint-example" personality i32* ()* @fake_personality_function {
; CHECK-LABEL: @test_no_base_use_invoke(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[D:%.*]] = getelementptr i32, i32 addrspace(1)* [[B:%.*]], i64 16
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"(i32 addrspace(1)* [[B]], i32 addrspace(1)* [[D]]) ]
; CHECK-NEXT: to label [[NORMAL_DEST:%.*]] unwind label [[UNWIND_DEST:%.*]]
; CHECK: normal_dest:
; CHECK-NEXT: [[D_NEW_1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 1)
; CHECK-NEXT: store i32 1, i32 addrspace(1)* [[D_NEW_1]], align 4
; CHECK-NEXT: ret void
; CHECK: unwind_dest:
; CHECK-NEXT: [[LPAD:%.*]] = landingpad token
; CHECK-NEXT: cleanup
; CHECK-NEXT: [[LPD_NEW_1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LPAD]], i32 0, i32 1)
; CHECK-NEXT: store i32 2, i32 addrspace(1)* [[LPD_NEW_1]], align 4
; CHECK-NEXT: ret void
;
entry:
%d = getelementptr i32, i32 addrspace(1)* %b, i64 16
%safepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live" (i32 addrspace(1)* %b, i32 addrspace(1)* %b, i32 addrspace(1)* %d, i32 addrspace(1)* %d)]
to label %normal_dest unwind label %unwind_dest
normal_dest:
%b.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%b.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 1)
%d.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 2)
%d.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 3)
%d.new.3 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 1, i32 2)
%d.new.4 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 1, i32 3)
store i32 1, i32 addrspace(1)* %d.new.1
ret void
unwind_dest:
%lpad = landingpad token
cleanup
%lpb.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 0)
%lpb.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 1)
%lpd.new.1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 2)
%lpd.new.2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 3)
%lpd.new.3 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 1, i32 2)
%lpd.new.4 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 1, i32 3)
store i32 2, i32 addrspace(1)* %lpd.new.1
ret void
}
declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...)
declare i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token, i32, i32)

View File

@ -1,51 +1,180 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -instcombine -S | FileCheck %s
; These tests check the optimizations specific to
; pointers being relocated at a statepoint.
declare i32* @fake_personality_function()
declare void @func()
define i1 @test_negative(i32 addrspace(1)* %p) gc "statepoint-example" {
; CHECK-LABEL: @test_negative(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"(i32 addrspace(1)* [[P:%.*]]) ]
; CHECK-NEXT: [[PNEW:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 addrspace(1)* [[PNEW]], null
; CHECK-NEXT: ret i1 [[CMP]]
;
entry:
%safepoint_token = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live"(i32 addrspace(1)* %p)]
%pnew = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%cmp = icmp eq i32 addrspace(1)* %pnew, null
ret i1 %cmp
; CHECK-LABEL: test_negative
; CHECK: %pnew = call i32 addrspace(1)*
; CHECK: ret i1 %cmp
}
define i1 @test_nonnull(i32 addrspace(1)* nonnull %p) gc "statepoint-example" {
; CHECK-LABEL: @test_nonnull(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"() ]
; CHECK-NEXT: ret i1 false
;
entry:
%safepoint_token = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live"(i32 addrspace(1)* %p)]
%pnew = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%cmp = icmp eq i32 addrspace(1)* %pnew, null
ret i1 %cmp
; CHECK-LABEL: test_nonnull
; CHECK: ret i1 false
}
define i1 @test_null() gc "statepoint-example" {
; CHECK-LABEL: @test_null(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"() ]
; CHECK-NEXT: ret i1 true
;
entry:
%safepoint_token = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live"(i32 addrspace(1)* null)]
%pnew = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%cmp = icmp eq i32 addrspace(1)* %pnew, null
ret i1 %cmp
; CHECK-LABEL: test_null
; CHECK-NOT: %pnew
; CHECK: ret i1 true
}
define i1 @test_undef() gc "statepoint-example" {
; CHECK-LABEL: @test_undef(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"() ]
; CHECK-NEXT: ret i1 undef
;
entry:
%safepoint_token = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live"(i32 addrspace(1)* undef)]
%pnew = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%cmp = icmp eq i32 addrspace(1)* %pnew, null
ret i1 %cmp
; CHECK-LABEL: test_undef
; CHECK-NOT: %pnew
; CHECK: ret i1 undef
}
define i1 @test_negative_invoke(i32 addrspace(1)* %p) gc "statepoint-example" personality i32* ()* @fake_personality_function {
; CHECK-LABEL: @test_negative_invoke(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"(i32 addrspace(1)* [[P:%.*]]) ]
; CHECK-NEXT: to label [[NORMAL_DEST:%.*]] unwind label [[UNWIND_DEST:%.*]]
; CHECK: normal_dest:
; CHECK-NEXT: [[PNEW:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 addrspace(1)* [[PNEW]], null
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK: unwind_dest:
; CHECK-NEXT: [[LPAD:%.*]] = landingpad token
; CHECK-NEXT: cleanup
; CHECK-NEXT: [[PNEW2:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LPAD]], i32 0, i32 0)
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 addrspace(1)* [[PNEW2]], null
; CHECK-NEXT: ret i1 [[CMP2]]
;
entry:
%safepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live"(i32 addrspace(1)* %p)]
to label %normal_dest unwind label %unwind_dest
normal_dest:
%pnew = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%cmp = icmp eq i32 addrspace(1)* %pnew, null
ret i1 %cmp
unwind_dest:
%lpad = landingpad token
cleanup
%pnew2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 0)
%cmp2 = icmp ne i32 addrspace(1)* %pnew2, null
ret i1 %cmp2
}
define i1 @test_nonnull_invoke(i32 addrspace(1)* nonnull %p) gc "statepoint-example" personality i32* ()* @fake_personality_function {
; CHECK-LABEL: @test_nonnull_invoke(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"() ]
; CHECK-NEXT: to label [[NORMAL_DEST:%.*]] unwind label [[UNWIND_DEST:%.*]]
; CHECK: normal_dest:
; CHECK-NEXT: ret i1 false
; CHECK: unwind_dest:
; CHECK-NEXT: [[LPAD:%.*]] = landingpad token
; CHECK-NEXT: cleanup
; CHECK-NEXT: ret i1 true
;
entry:
%safepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live"(i32 addrspace(1)* %p)]
to label %normal_dest unwind label %unwind_dest
normal_dest:
%pnew = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%cmp = icmp eq i32 addrspace(1)* %pnew, null
ret i1 %cmp
unwind_dest:
%lpad = landingpad token
cleanup
%pnew2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 0)
%cmp2 = icmp ne i32 addrspace(1)* %pnew2, null
ret i1 %cmp2
}
define i1 @test_null_invoke() gc "statepoint-example" personality i32* ()* @fake_personality_function {
; CHECK-LABEL: @test_null_invoke(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"() ]
; CHECK-NEXT: to label [[NORMAL_DEST:%.*]] unwind label [[UNWIND_DEST:%.*]]
; CHECK: normal_dest:
; CHECK-NEXT: ret i1 true
; CHECK: unwind_dest:
; CHECK-NEXT: [[LPAD:%.*]] = landingpad token
; CHECK-NEXT: cleanup
; CHECK-NEXT: ret i1 false
;
entry:
%safepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live"(i32 addrspace(1)* null)]
to label %normal_dest unwind label %unwind_dest
normal_dest:
%pnew = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%cmp = icmp eq i32 addrspace(1)* %pnew, null
ret i1 %cmp
unwind_dest:
%lpad = landingpad token
cleanup
%pnew2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 0)
%cmp2 = icmp ne i32 addrspace(1)* %pnew2, null
ret i1 %cmp2
}
define i1 @test_undef_invoke() gc "statepoint-example" personality i32* ()* @fake_personality_function {
; CHECK-LABEL: @test_undef_invoke(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* nonnull @func, i32 0, i32 0, i32 0, i32 0) [ "gc-live"() ]
; CHECK-NEXT: to label [[NORMAL_DEST:%.*]] unwind label [[UNWIND_DEST:%.*]]
; CHECK: normal_dest:
; CHECK-NEXT: ret i1 undef
; CHECK: unwind_dest:
; CHECK-NEXT: [[LPAD:%.*]] = landingpad token
; CHECK-NEXT: cleanup
; CHECK-NEXT: ret i1 undef
;
entry:
%safepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) ["gc-live"(i32 addrspace(1)* undef)]
to label %normal_dest unwind label %unwind_dest
normal_dest:
%pnew = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0)
%cmp = icmp eq i32 addrspace(1)* %pnew, null
ret i1 %cmp
unwind_dest:
%lpad = landingpad token
cleanup
%pnew2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %lpad, i32 0, i32 0)
%cmp2 = icmp ne i32 addrspace(1)* %pnew2, null
ret i1 %cmp2
}
declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...)