forked from OSchip/llvm-project
[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:
parent
e7e67c930a
commit
a0ff0f30df
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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, ...)
|
||||
|
|
Loading…
Reference in New Issue