1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 18:42:46 +02:00

[InstCombine] Move handling of gc.relocate in a gc.statepoint

The only def for gc.relocate is a gc.statepoint. But real dependency is not
described by def-use chain. Instead this dependency is encoded by indecies
of operands in gc-live bundle of statepoint as integer constants in gc.relocate.

InstCombine operates by def-use chain. As a result when value in gc-live bundle
is simplified the gc.statepoint itself is not simplified but it might simplify dependent
gc.relocates. To trigger the optimization of gc.relocate we now unconditionally trigger
check of all dependent gc.relocates by adding them to worklist.

This CL handles of gc.relocates as process of gc.statepoint optimization considering
gc.statepoint and related gc.relocate as whole entity.

Reviewers: reames, dantrushin
Reviewed By: reames
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D85954
This commit is contained in:
Serguei Katkov 2020-08-12 16:09:02 +07:00
parent 1bc410be68
commit 41dd411c5b

View File

@ -1488,78 +1488,60 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
} }
case Intrinsic::experimental_gc_statepoint: { case Intrinsic::experimental_gc_statepoint: {
auto &GCSP = *cast<GCStatepointInst>(II); auto &GCSP = *cast<GCStatepointInst>(II);
// Let's we have the following case: for (const GCRelocateInst *Reloc : GCSP.getGCRelocates()) {
// A = gc.relocate(null) GCRelocateInst &GCR = *const_cast<GCRelocateInst *>(Reloc);
// B = statepoint(A)
// C = gc.relocate(A)
// A will be substituted with null and its user B will be added to worklist.
// Statepoint B is not simplified and if C was considered before it will be
// re-considered after simplification of A.
// To resolve this case while processing statepoint B we add all gc.relocate
// users to worklist to give a chance to be simplified to null.
// This is to reduce the number of InstCombine iteration.
// Actually C can be transformed on the next iteration.
// chains in one iteration.
// TODO: we can handle relocation here, it will reduce the number of
// relocations to re-consider and also helps to reduce the number of
// gc live pointers in statepoint instruction bundle.
for (const GCRelocateInst *Reloc : GCSP.getGCRelocates())
Worklist.add(const_cast<GCRelocateInst *>(Reloc));
break;
}
case Intrinsic::experimental_gc_relocate: {
auto &GCR = *cast<GCRelocateInst>(II);
// If we have two copies of the same pointer in the statepoint argument // Remove the relocation if unused.
// list, canonicalize to one. This may let us common gc.relocates. if (GCR.use_empty()) {
if (GCR.getBasePtr() == GCR.getDerivedPtr() && eraseInstFromFunction(GCR);
GCR.getBasePtrIndex() != GCR.getDerivedPtrIndex()) { continue;
auto *OpIntTy = GCR.getOperand(2)->getType();
return replaceOperand(*II, 2,
ConstantInt::get(OpIntTy, GCR.getBasePtrIndex()));
}
// Translate facts known about a pointer before relocating into
// facts about the relocate value, while being careful to
// preserve relocation semantics.
Value *DerivedPtr = GCR.getDerivedPtr();
// Remove the relocation if unused, note that this check is required
// to prevent the cases below from looping forever.
if (II->use_empty())
return eraseInstFromFunction(*II);
// Undef is undef, even after relocation.
// TODO: provide a hook for this in GCStrategy. This is clearly legal for
// most practical collectors, but there was discussion in the review thread
// about whether it was legal for all possible collectors.
if (isa<UndefValue>(DerivedPtr))
// Use undef of gc_relocate's type to replace it.
return replaceInstUsesWith(*II, UndefValue::get(II->getType()));
if (auto *PT = dyn_cast<PointerType>(II->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.
return replaceInstUsesWith(*II, ConstantPointerNull::get(PT));
// isKnownNonNull -> nonnull attribute
if (!II->hasRetAttr(Attribute::NonNull) &&
isKnownNonZero(DerivedPtr, DL, 0, &AC, II, &DT)) {
II->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull);
return II;
} }
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, ...)
} }
// 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, ...)
break; break;
} }
case Intrinsic::experimental_guard: { case Intrinsic::experimental_guard: {
// Is this guard followed by another guard? We scan forward over a small // Is this guard followed by another guard? We scan forward over a small
// fixed window of instructions to handle common cases with conditions // fixed window of instructions to handle common cases with conditions