|
21 | 21 | #include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
|
22 | 22 | #include "swift/SILOptimizer/Analysis/ValueTracking.h"
|
23 | 23 | #include "swift/SILOptimizer/Utils/CFGOptUtils.h"
|
| 24 | +#include "swift/SILOptimizer/Utils/DebugOptUtils.h" |
24 | 25 | #include "swift/SILOptimizer/Utils/InstOptUtils.h"
|
25 | 26 | #include "llvm/ADT/DenseMap.h"
|
26 | 27 | #include "llvm/ADT/SmallPtrSet.h"
|
@@ -95,16 +96,120 @@ SILCombiner::visitRefToRawPointerInst(RefToRawPointerInst *rrpi) {
|
95 | 96 | return nullptr;
|
96 | 97 | }
|
97 | 98 |
|
98 |
| -SILInstruction *SILCombiner::visitUpcastInst(UpcastInst *UCI) { |
99 |
| - if (UCI->getFunction()->hasOwnership()) |
| 99 | +namespace { |
| 100 | + |
| 101 | +/// A folder object for sequences of forwarding instructions that forward owned |
| 102 | +/// ownership. Is used to detect if we can delete the intermediate forwarding |
| 103 | +/// instructions without ownership issues and then allows the user to either |
| 104 | +/// delete all of the rest of the forwarding instructions and then replace front |
| 105 | +/// with a new value or set front's operand to a new value. |
| 106 | +class SingleBlockOwnedForwardingInstFolder { |
| 107 | + SmallVector<SingleValueInstruction *, 4> rest; |
| 108 | + SILCombiner &SC; |
| 109 | + SingleValueInstruction *front; |
| 110 | + |
| 111 | +public: |
| 112 | + SingleBlockOwnedForwardingInstFolder( |
| 113 | + SILCombiner &SC, SingleValueInstruction *instructionToFold) |
| 114 | + : SC(SC), front(instructionToFold) { |
| 115 | + // If our initial instruction to fold isn't owned, set it to nullptr to |
| 116 | + // indicate invalid. |
| 117 | + if (SILValue(instructionToFold).getOwnershipKind() != OwnershipKind::Owned) |
| 118 | + front = nullptr; |
| 119 | + } |
| 120 | + |
| 121 | + bool isValid() const { return bool(front); } |
| 122 | + |
| 123 | + bool add(SingleValueInstruction *next) { |
| 124 | + assert(isValid()); |
| 125 | + if (SILValue(next).getOwnershipKind() != OwnershipKind::Owned) |
| 126 | + return false; |
| 127 | + |
| 128 | + if (next->getSingleUse()) { |
| 129 | + rest.push_back(next); |
| 130 | + return true; |
| 131 | + } |
| 132 | + |
| 133 | + if (front->getParent() != next->getParent()) { |
| 134 | + return false; |
| 135 | + } |
| 136 | + |
| 137 | + // Otherwise, since the two values are in the same block and we want to |
| 138 | + // optimize only if our original value doesn't have any non-debug uses, we |
| 139 | + // know that our value can only have a single non-debug use, the consuming |
| 140 | + // user. So if we are not in that situation, bail. |
| 141 | + if (!hasOneNonDebugUse(next)) |
| 142 | + return false; |
| 143 | + |
| 144 | + rest.push_back(next); |
| 145 | + return true; |
| 146 | + } |
| 147 | + |
| 148 | + /// Delete all forwarding uses and then RAUW front with newValue. |
| 149 | + SingleValueInstruction *optimizeWithReplacement(SILValue newValue) && { |
| 150 | + // NOTE: Even though after running cleanup rest, front now has its |
| 151 | + // forwarding operand set to Undef, we haven't touched its result. So it is |
| 152 | + // safe to RAUW. |
| 153 | + cleanupRest(); |
| 154 | + SC.replaceValueUsesWith(front, newValue); |
| 155 | + return nullptr; |
| 156 | + } |
| 157 | + |
| 158 | + /// Delete all forwarding uses and then set front's first operand to be \p |
| 159 | + /// newValue. |
| 160 | + SingleValueInstruction *optimizeWithSetValue(SILValue newValue) && { |
| 161 | + cleanupRest(); |
| 162 | + assert(isa<SILUndef>(front->getOperand(0))); |
| 163 | + front->setOperand(0, newValue); |
| 164 | + SC.setUseValue(&front->getOperandRef(0), newValue); |
100 | 165 | return nullptr;
|
| 166 | + } |
| 167 | + |
| 168 | +private: |
| 169 | + /// From backwards -> forwards, for each instruction in rest, delete all of |
| 170 | + /// its debug uses and then set its single remaining use to be SILUndef. |
| 171 | + /// |
| 172 | + /// This means that after this runs front's forwarding operand is now |
| 173 | + /// SILUndef. |
| 174 | + void cleanupRest() & { |
| 175 | + // We process backwards -> forwards. This cleans up everything but the front |
| 176 | + // value. |
| 177 | + while (!rest.empty()) { |
| 178 | + auto *inst = rest.pop_back_val(); |
| 179 | + deleteAllDebugUses(inst, SC.getInstModCallbacks()); |
| 180 | + auto *next = inst->getSingleUse(); |
| 181 | + assert(next); |
| 182 | + assert(rest.empty() || bool(next->getUser() == rest.back())); |
| 183 | + next->set(SILUndef::get(next->get()->getType(), inst->getModule())); |
| 184 | + SC.eraseInstFromFunction(*inst); |
| 185 | + } |
| 186 | + } |
| 187 | +}; |
101 | 188 |
|
102 |
| - // Ref to raw pointer consumption of other ref casts. |
| 189 | +} // namespace |
| 190 | + |
| 191 | +SILInstruction *SILCombiner::visitUpcastInst(UpcastInst *uci) { |
| 192 | + auto operand = uci->getOperand(); |
| 193 | + |
| 194 | + // %operandUpcast = upcast %0 : $X->Y |
| 195 | + // %upcastInst = upcast %operandUpcast : $Y->Z |
| 196 | + // |
| 197 | + // %operandUpcast = upcast %0 : $X->Y |
| 198 | + // %1 = upcast %0 : $X->Z |
103 | 199 | //
|
104 |
| - // (upcast (upcast x)) -> (upcast x) |
105 |
| - if (auto *Op = dyn_cast<UpcastInst>(UCI->getOperand())) { |
106 |
| - UCI->setOperand(Op->getOperand()); |
107 |
| - return Op->use_empty() ? eraseInstFromFunction(*Op) : nullptr; |
| 200 | + // If operandUpcast does not have any further uses, we delete it. |
| 201 | + if (auto *operandAsUpcast = dyn_cast<UpcastInst>(operand)) { |
| 202 | + if (operand.getOwnershipKind() != OwnershipKind::Owned) { |
| 203 | + uci->setOperand(operandAsUpcast->getOperand()); |
| 204 | + return operandAsUpcast->use_empty() |
| 205 | + ? eraseInstFromFunction(*operandAsUpcast) |
| 206 | + : nullptr; |
| 207 | + } |
| 208 | + SingleBlockOwnedForwardingInstFolder folder(*this, uci); |
| 209 | + if (folder.add(operandAsUpcast)) { |
| 210 | + return std::move(folder).optimizeWithSetValue( |
| 211 | + operandAsUpcast->getOperand()); |
| 212 | + } |
108 | 213 | }
|
109 | 214 |
|
110 | 215 | return nullptr;
|
@@ -282,35 +387,92 @@ SILCombiner::visitUncheckedAddrCastInst(UncheckedAddrCastInst *UADCI) {
|
282 | 387 | }
|
283 | 388 |
|
284 | 389 | SILInstruction *
|
285 |
| -SILCombiner::visitUncheckedRefCastInst(UncheckedRefCastInst *URCI) { |
286 |
| - if (URCI->getFunction()->hasOwnership()) |
287 |
| - return nullptr; |
| 390 | +SILCombiner::visitUncheckedRefCastInst(UncheckedRefCastInst *urci) { |
| 391 | + // %0 = unchecked_ref_cast %x : $X->Y |
| 392 | + // %1 = unchecked_ref_cast %0 : $Y->Z |
| 393 | + // |
| 394 | + // -> |
| 395 | + // |
| 396 | + // %0 = unchecked_ref_cast %x : $X->Y |
| 397 | + // %1 = unchecked_ref_cast %x : $X->Z |
| 398 | + // |
| 399 | + // NOTE: For owned values, we only perform this optimization if we can |
| 400 | + // guarantee that we can eliminate the initial unchecked_ref_cast. |
| 401 | + if (auto *otherURCI = dyn_cast<UncheckedRefCastInst>(urci->getOperand())) { |
| 402 | + SILValue otherURCIOp = otherURCI->getOperand(); |
| 403 | + if (otherURCIOp.getOwnershipKind() != OwnershipKind::Owned) { |
| 404 | + return Builder.createUncheckedRefCast(urci->getLoc(), otherURCIOp, |
| 405 | + urci->getType()); |
| 406 | + } |
| 407 | + SingleBlockOwnedForwardingInstFolder folder(*this, urci); |
| 408 | + if (folder.add(otherURCI)) { |
| 409 | + auto *newValue = Builder.createUncheckedRefCast( |
| 410 | + urci->getLoc(), otherURCIOp, urci->getType()); |
| 411 | + return std::move(folder).optimizeWithReplacement(newValue); |
| 412 | + } |
| 413 | + } |
288 | 414 |
|
289 |
| - // (unchecked-ref-cast (unchecked-ref-cast x X->Y) Y->Z) |
290 |
| - // -> |
291 |
| - // (unchecked-ref-cast x X->Z) |
292 |
| - if (auto *OtherURCI = dyn_cast<UncheckedRefCastInst>(URCI->getOperand())) |
293 |
| - return Builder.createUncheckedRefCast(URCI->getLoc(), |
294 |
| - OtherURCI->getOperand(), |
295 |
| - URCI->getType()); |
296 |
| - |
297 |
| - // (unchecked_ref_cast (upcast x X->Y) Y->Z) -> (unchecked_ref_cast x X->Z) |
298 |
| - if (auto *UI = dyn_cast<UpcastInst>(URCI->getOperand())) |
299 |
| - return Builder.createUncheckedRefCast(URCI->getLoc(), |
300 |
| - UI->getOperand(), |
301 |
| - URCI->getType()); |
302 |
| - |
303 |
| - if (URCI->getType() != URCI->getOperand()->getType() && |
304 |
| - URCI->getType().isExactSuperclassOf(URCI->getOperand()->getType())) |
305 |
| - return Builder.createUpcast(URCI->getLoc(), URCI->getOperand(), |
306 |
| - URCI->getType()); |
307 |
| - |
308 |
| - // (unchecked_ref_cast (open_existential_ref (init_existential_ref X))) -> |
309 |
| - // (unchecked_ref_cast X) |
310 |
| - if (auto *OER = dyn_cast<OpenExistentialRefInst>(URCI->getOperand())) |
311 |
| - if (auto *IER = dyn_cast<InitExistentialRefInst>(OER->getOperand())) |
312 |
| - return Builder.createUncheckedRefCast(URCI->getLoc(), IER->getOperand(), |
313 |
| - URCI->getType()); |
| 415 | + // %0 = upcast %x : $X->Y |
| 416 | + // %1 = unchecked_ref_cast %0 : $Y->Z |
| 417 | + // |
| 418 | + // -> |
| 419 | + // |
| 420 | + // %0 = upcast %x : $X->Y |
| 421 | + // %1 = unchecked_ref_cast %x : $X->Z |
| 422 | + // |
| 423 | + // NOTE: For owned values, we only perform this optimization if we can |
| 424 | + // guarantee that we can eliminate the upcast. |
| 425 | + if (auto *ui = dyn_cast<UpcastInst>(urci->getOperand())) { |
| 426 | + SILValue uiOp = ui->getOperand(); |
| 427 | + |
| 428 | + if (uiOp.getOwnershipKind() != OwnershipKind::Owned) { |
| 429 | + return Builder.createUncheckedRefCast(urci->getLoc(), uiOp, |
| 430 | + urci->getType()); |
| 431 | + } |
| 432 | + |
| 433 | + SingleBlockOwnedForwardingInstFolder folder(*this, urci); |
| 434 | + if (folder.add(ui)) { |
| 435 | + auto *newValue = |
| 436 | + Builder.createUncheckedRefCast(urci->getLoc(), uiOp, urci->getType()); |
| 437 | + return std::move(folder).optimizeWithReplacement(newValue); |
| 438 | + } |
| 439 | + } |
| 440 | + |
| 441 | + // This is an exact transform where we are replacing urci with an upcast on |
| 442 | + // the same value. So from an ownership perspective because both instructions |
| 443 | + // are forwarding and we are eliminating urci, we are safe. |
| 444 | + if (urci->getType() != urci->getOperand()->getType() && |
| 445 | + urci->getType().isExactSuperclassOf(urci->getOperand()->getType())) |
| 446 | + return Builder.createUpcast(urci->getLoc(), urci->getOperand(), |
| 447 | + urci->getType()); |
| 448 | + |
| 449 | + // %0 = init_existential_ref %x : $X -> Existential |
| 450 | + // %1 = open_existential_ref %0 : $Existential -> @opened() Existential |
| 451 | + // %2 = unchecked_ref_cast %1 |
| 452 | + // |
| 453 | + // -> |
| 454 | + // |
| 455 | + // %0 = init_existential_ref %x : $X -> Existential |
| 456 | + // %1 = open_existential_ref %0 : $Existential -> @opened() Existential |
| 457 | + // %2 = unchecked_ref_cast %x |
| 458 | + // |
| 459 | + // NOTE: When we have an owned value, we only perform this optimization if we |
| 460 | + // can remove both the open_existential_ref and the init_existential_ref. |
| 461 | + if (auto *oer = dyn_cast<OpenExistentialRefInst>(urci->getOperand())) { |
| 462 | + if (auto *ier = dyn_cast<InitExistentialRefInst>(oer->getOperand())) { |
| 463 | + if (ier->getOwnershipKind() != OwnershipKind::Owned) { |
| 464 | + return Builder.createUncheckedRefCast(urci->getLoc(), ier->getOperand(), |
| 465 | + urci->getType()); |
| 466 | + } |
| 467 | + |
| 468 | + SingleBlockOwnedForwardingInstFolder folder(*this, urci); |
| 469 | + if (folder.add(oer) && folder.add(ier)) { |
| 470 | + auto *newValue = Builder.createUncheckedRefCast( |
| 471 | + urci->getLoc(), ier->getOperand(), urci->getType()); |
| 472 | + return std::move(folder).optimizeWithReplacement(newValue); |
| 473 | + } |
| 474 | + } |
| 475 | + } |
314 | 476 |
|
315 | 477 | return nullptr;
|
316 | 478 | }
|
|
0 commit comments