Index: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -1672,104 +1672,6 @@ AC.updateAffectedValues(II); break; } - case Intrinsic::experimental_gc_statepoint: { - GCStatepointInst &GCSP = *cast(II); - SmallPtrSet LiveGcValues; - for (const GCRelocateInst *Reloc : GCSP.getGCRelocates()) { - GCRelocateInst &GCR = *const_cast(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(DerivedPtr) || isa(BasePtr)) { - replaceInstUsesWith(GCR, UndefValue::get(GCR.getType())); - eraseInstFromFunction(GCR); - continue; - } - - if (auto *PT = dyn_cast(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(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 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 Val2Idx; - std::vector 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(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); - if (isa(II)) - return CallInst::CreateWithReplacedBundle(cast(II), NewBundle); - else - return InvokeInst::CreateWithReplacedBundle(cast(II), - NewBundle); - break; - } 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 @@ -1905,6 +1807,8 @@ 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); } @@ -2267,6 +2171,109 @@ 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(&Call); + SmallPtrSet LiveGcValues; + for (const GCRelocateInst *Reloc : GCSP.getGCRelocates()) { + GCRelocateInst &GCR = *const_cast(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(DerivedPtr) || isa(BasePtr)) { + replaceInstUsesWith(GCR, UndefValue::get(GCR.getType())); + eraseInstFromFunction(GCR); + continue; + } + + if (auto *PT = dyn_cast(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(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 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 Val2Idx; + std::vector 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(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); + if (isa(&Call)) + return CallInst::CreateWithReplacedBundle(cast(&Call), + NewBundle); + else + return InvokeInst::CreateWithReplacedBundle(cast(&Call), + NewBundle); + break; + } + } + return Changed ? &Call : nullptr; } Index: llvm/test/Transforms/InstCombine/statepoint-cleanup.ll =================================================================== --- llvm/test/Transforms/InstCombine/statepoint-cleanup.ll +++ llvm/test/Transforms/InstCombine/statepoint-cleanup.ll @@ -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 @@ 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) Index: llvm/test/Transforms/InstCombine/statepoint.ll =================================================================== --- llvm/test/Transforms/InstCombine/statepoint.ll +++ llvm/test/Transforms/InstCombine/statepoint.ll @@ -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, ...)