|
31 | 31 | #include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" |
32 | 32 | #include "swift/SILOptimizer/Utils/CanonicalizeInstruction.h" |
33 | 33 | #include "swift/SILOptimizer/Utils/InstOptUtils.h" |
| 34 | +#include "swift/SILOptimizer/Utils/InstructionDeleter.h" |
34 | 35 | #include "llvm/ADT/PostOrderIterator.h" |
35 | 36 |
|
36 | 37 | using namespace swift; |
@@ -107,6 +108,7 @@ namespace { |
107 | 108 | struct SILGenCleanup : SILModuleTransform { |
108 | 109 | void run() override; |
109 | 110 |
|
| 111 | + bool fixupBorrowAccessors(SILFunction *function); |
110 | 112 | bool completeOSSALifetimes(SILFunction *function); |
111 | 113 | template <typename Range> |
112 | 114 | bool completeLifetimesInRange(Range const &range, |
@@ -229,6 +231,87 @@ void collectReachableRoots(SILFunction *function, BasicBlockWorklist &backward, |
229 | 231 | } while (changed); |
230 | 232 | } |
231 | 233 |
|
| 234 | +/* SILGen may produce a borrow accessor result from within a local borrow |
| 235 | + * scope. Such as: |
| 236 | + * |
| 237 | + * ``` |
| 238 | + * %ld = load_borrow %self |
| 239 | + * %fwd = unchecked_ownership %ld |
| 240 | + * %ex = struct_extract %fwd, #Struct.storedProperty |
| 241 | + * end_borrow %ld |
| 242 | + * return %ex |
| 243 | + * ``` |
| 244 | + * This is illegal OSSA, since the return uses a value outside it's borrow |
| 245 | + * scope. |
| 246 | + * |
| 247 | + * Transform this into valid OSSA: |
| 248 | + * |
| 249 | + * ``` |
| 250 | + * %ld = load_borrow %self |
| 251 | + * %ex = struct_extract %ld, #Struct.storedProperty |
| 252 | + * return_borrow %ex from_scopes %ld |
| 253 | + * ``` |
| 254 | + */ |
| 255 | +bool SILGenCleanup::fixupBorrowAccessors(SILFunction *function) { |
| 256 | + if (!function->getConventions().hasGuaranteedResult()) { |
| 257 | + return false; |
| 258 | + } |
| 259 | + auto returnBB = function->findReturnBB(); |
| 260 | + if (returnBB == function->end()) { |
| 261 | + return false; |
| 262 | + } |
| 263 | + |
| 264 | + auto *returnInst = cast<ReturnInst>(returnBB->getTerminator()); |
| 265 | + if (returnInst->getOperand()->getOwnershipKind() != |
| 266 | + OwnershipKind::Guaranteed) { |
| 267 | + return false; |
| 268 | + } |
| 269 | + |
| 270 | + SmallVector<SILValue, 8> enclosingValues; |
| 271 | + findGuaranteedReferenceRoots(returnInst->getOperand(), |
| 272 | + /*lookThroughNestedBorrows=*/false, |
| 273 | + enclosingValues); |
| 274 | + SmallVector<SILInstruction *> scopeEnds; |
| 275 | + SmallVector<SILValue> operands; |
| 276 | + SmallVector<SILInstruction *> toDelete; |
| 277 | + |
| 278 | + // For all the local borrow scopes that enclose the return value, delete |
| 279 | + // their end_borrow instructions and use them as an encolsing value in |
| 280 | + // return_borrow instruction. |
| 281 | + for (auto enclosingValue : enclosingValues) { |
| 282 | + BorrowedValue borrow(enclosingValue); |
| 283 | + if (!borrow.isLocalScope()) { |
| 284 | + continue; |
| 285 | + } |
| 286 | + borrow.getLocalScopeEndingInstructions(scopeEnds); |
| 287 | + for (auto *scopeEnd : scopeEnds) { |
| 288 | + if (auto *endBorrow = dyn_cast<EndBorrowInst>(scopeEnd)) { |
| 289 | + operands.push_back(endBorrow->getOperand()); |
| 290 | + endBorrow->eraseFromParent(); |
| 291 | + } |
| 292 | + } |
| 293 | + for (auto *uncheckedOwnership : |
| 294 | + borrow->getUsersOfType<UncheckedOwnershipInst>()) { |
| 295 | + uncheckedOwnership->replaceAllUsesWith(*borrow); |
| 296 | + toDelete.push_back(uncheckedOwnership); |
| 297 | + } |
| 298 | + } |
| 299 | + |
| 300 | + for (auto *inst : toDelete) { |
| 301 | + inst->eraseFromParent(); |
| 302 | + } |
| 303 | + |
| 304 | + if (operands.empty()) { |
| 305 | + return false; |
| 306 | + } |
| 307 | + |
| 308 | + SILBuilderWithScope(returnInst) |
| 309 | + .createReturnBorrow(returnInst->getLoc(), returnInst->getOperand(), |
| 310 | + operands); |
| 311 | + returnInst->eraseFromParent(); |
| 312 | + return true; |
| 313 | +} |
| 314 | + |
232 | 315 | bool SILGenCleanup::completeOSSALifetimes(SILFunction *function) { |
233 | 316 | if (!getModule()->getOptions().OSSACompleteLifetimes) |
234 | 317 | return false; |
@@ -339,7 +422,8 @@ void SILGenCleanup::run() { |
339 | 422 | LLVM_DEBUG(llvm::dbgs() |
340 | 423 | << "\nRunning SILGenCleanup on " << function.getName() << "\n"); |
341 | 424 |
|
342 | | - bool changed = completeOSSALifetimes(&function); |
| 425 | + bool changed = fixupBorrowAccessors(&function); |
| 426 | + changed |= completeOSSALifetimes(&function); |
343 | 427 | DeadEndBlocks deadEndBlocks(&function); |
344 | 428 | SILGenCanonicalize sgCanonicalize(deadEndBlocks); |
345 | 429 |
|
|
0 commit comments