//===--- AddressLowering.cpp - Lower SIL address-only types. --------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // // This pass lowers SILTypes. On completion, the SILType of every SILValue is // its SIL storage type. A SIL storage type is always an address type for values // that require indirect storage at the LLVM IR level. Consequently, this pass // is required for IRGen. It is a mandatory IRGen preparation pass (not a // diagnostic pass). // // In the following text, items marked "[REUSE]" only apply to the proposed // storage reuse optimization, which is not currently implemented. // // ## State // // A `valueStorageMap` maps each opaque SIL value to its storage // information containing: // // - An ordinal representing the position of this instruction. // // - [REUSE] The identifier of the storage object. An optimized storage object // may have multiple disjoint lifetimes. A storage object may also have // subobjects. Each subobject has its own live range. When considering // liveness of the subobject, one must also consider liveness of the // parent object. // // - If this is a subobject projection, refer back to the value whose // storage object will be the parent that this storage address is a // projection of. // // - The storage address for this subobject. // // ## Step #1: Map opaque values // // Populate `valueStorageMap` in forward order (RPO), giving each opaque value // an ordinal position. // // [REUSE] Assign a storage identifier to each opaque value. Optionally optimize // storage by assigning multiple values the same identifier. // // ## Step #2: Allocate storage // // In reverse order (PO), allocate the parent storage object for each opaque // value. // // [REUSE] If storage has already been allocated for the current live range, // then simply reuse it. // // If the value's use composes a parent object from this value, and use's // storage can be projected from, then mark the value's storage as a projection // from the use value. [REUSE] Also inherit the use's storage identifier, and // add an interval to the live range with the current projection path. // // A use can be projected from if its allocation is available at (dominates) // this value and using the same storage over the interval from this value to // the use does not overlap with the existing live range. // // Checking interference requires checking all operands that have been marked as // projections. In the case of block arguments, it means checking the terminator // operands of all predecessor blocks. // // [REUSE] Rather than checking all value operands, each live range will contain // a set of intervals. Each interval will be associated with a projection path. // // Opaque value's that are the root of all projection paths now have their // `storageAddress` assigned to an `alloc_stack` or argument. Opaque value's // that are projections do not yet have a `storageAddress`. // // ## Step #3. Rewrite opaque values // // In forward order (RPO), rewrite each opaque value definition, and all its // uses. This generally involves creating a new `_addr` variant of the // instruction and obtaining the storage address from the `valueStorageMap`. // // If this value's storage is a projection of the value defined by its composing // use, then first generate instructions to materialize the projection. This is // a recursive process starting with the root of the projection path. // // A projection path will be materialized once, for the leaf subobject. When // this happens, the `storageAddress` will be assigned for any intermediate // projection paths. When those values are rewritten, their `storageAddress` // will already be available. // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "address-lowering" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILVisitor.h" #include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/Local.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; using llvm::SmallSetVector; using llvm::PointerIntPair; llvm::cl::opt OptimizeOpaqueAddressLowering("optimize-opaque-address-lowering", llvm::cl::init(false)); // Visit all call results. // Stop when the visitor returns `false`. static void visitCallResults(ApplySite apply, llvm::function_ref visitor) { // FIXME: this entire implementation only really works for ApplyInst. auto applyInst = cast(apply); if (applyInst->getType().is()) { // TODO: MultiValueInstruction for (auto *operand : applyInst->getUses()) { if (auto extract = dyn_cast(operand->getUser())) if (!visitor(extract)) break; } } else visitor(applyInst); } //===----------------------------------------------------------------------===// // ValueStorageMap: Map Opaque/Resilient SILValues to abstract storage units. //===----------------------------------------------------------------------===// namespace { struct ValueStorage { enum { IsProjectionMask = 0x1, IsRewrittenMask = 0x2 }; PointerIntPair projectionAndFlags; /// The final address of this storage unit after rewriting the SIL. /// For values linked to their own storage, this is set during storage /// allocation. For projections, it is only set after instruction rewriting. SILValue storageAddress; bool isProjection() const { return projectionAndFlags.getInt() & IsProjectionMask; } /// Return the operand the composes an aggregate from this value. Operand *getComposedOperand() const { assert(isProjection()); return projectionAndFlags.getPointer(); } void setComposedOperand(Operand *oper) { projectionAndFlags.setPointer(oper); projectionAndFlags.setInt(projectionAndFlags.getInt() | IsProjectionMask); } bool isRewritten() const { if (projectionAndFlags.getInt() & IsRewrittenMask) { assert(storageAddress); return true; } return false; } void markRewritten() { projectionAndFlags.setInt(projectionAndFlags.getInt() | IsRewrittenMask); } }; /// Map each opaque/resilient SILValue to its abstract storage. /// O(1) membership test. /// O(n) iteration in RPO order. class ValueStorageMap { typedef std::vector> ValueVector; // Hash of values to ValueVector indices. typedef llvm::DenseMap ValueHashMap; ValueVector valueVector; ValueHashMap valueHashMap; public: bool empty() const { return valueVector.empty(); } void clear() { valueVector.clear(); valueHashMap.clear(); } ValueVector::iterator begin() { return valueVector.begin(); } ValueVector::iterator end() { return valueVector.end(); } ValueVector::reverse_iterator rbegin() { return valueVector.rbegin(); } ValueVector::reverse_iterator rend() { return valueVector.rend(); } bool contains(SILValue value) const { return valueHashMap.find(value) != valueHashMap.end(); } unsigned getOrdinal(SILValue value) { auto hashIter = valueHashMap.find(value); assert(hashIter != valueHashMap.end() && "Missing SILValue"); return hashIter->second; } ValueStorage &getStorage(SILValue value) { return valueVector[getOrdinal(value)].second; } // This must be called in RPO order. ValueStorage &insertValue(SILValue value) { auto hashResult = valueHashMap.insert(std::make_pair(value, valueVector.size())); (void)hashResult; assert(hashResult.second && "SILValue already mapped"); valueVector.emplace_back(value, ValueStorage()); return valueVector.back().second; } }; } // end anonymous namespace //===----------------------------------------------------------------------===// // AddressLoweringState: shared state for the pass's analysis and transforms. //===----------------------------------------------------------------------===// namespace { struct AddressLoweringState { SILFunction *F; SILFunctionConventions loweredFnConv; // Dominators remain valid throughout this pass. DominanceInfo *domInfo; // All opaque values and associated storage. ValueStorageMap valueStorageMap; // All call sites with formally indirect SILArgument or SILResult conventions. // Calls are removed from the set when rewritten. SmallSetVector indirectApplies; // All function-exiting terminators (return or throw instructions). SmallVector returnInsts; // Delete these instructions after performing transformations. // They must not have any remaining users. SmallSetVector instsToDelete; AddressLoweringState(SILFunction *F, DominanceInfo *domInfo) : F(F), loweredFnConv(F->getLoweredFunctionType(), SILModuleConventions::getLoweredAddressConventions()), domInfo(domInfo) {} bool isDead(SILInstruction *inst) const { return instsToDelete.count(inst); } void markDead(SILInstruction *inst) { #ifndef NDEBUG for (auto result : inst->getResults()) for (Operand *use : result->getUses()) assert(instsToDelete.count(use->getUser())); #endif instsToDelete.insert(inst); } }; } // end anonymous namespace //===----------------------------------------------------------------------===// // OpaqueValueVisitor: Map OpaqueValues to ValueStorage. //===----------------------------------------------------------------------===// namespace { /// Collect all opaque/resilient values, inserting them in `valueStorageMap` in /// RPO order. /// /// Collect all call arguments with formally indirect SIL argument convention in /// `indirectOperands` and formally indirect SIL results in `indirectResults`. /// /// TODO: Perform linear-scan style in-place stack slot coloring by keeping /// track of each value's last use. class OpaqueValueVisitor { AddressLoweringState &pass; PostOrderFunctionInfo postorderInfo; public: explicit OpaqueValueVisitor(AddressLoweringState &pass) : pass(pass), postorderInfo(pass.F) {} void mapValueStorage(); protected: void visitApply(ApplySite applySite); void visitValue(SILValue value); }; } // end anonymous namespace /// Top-level entry: Populate `valueStorageMap`, `indirectResults`, and /// `indirectOperands`. /// /// Find all Opaque/Resilient SILValues and add them /// to valueStorageMap in RPO. void OpaqueValueVisitor::mapValueStorage() { for (auto *BB : postorderInfo.getReversePostOrder()) { if (BB->getTerminator()->isFunctionExiting()) pass.returnInsts.push_back(BB->getTerminator()); // Opaque function arguments have already been replaced. if (BB != pass.F->getEntryBlock()) { for (auto argI = BB->args_begin(), argEnd = BB->args_end(); argI != argEnd; ++argI) { visitValue(*argI); } } for (auto &II : *BB) { if (auto apply = ApplySite::isa(&II)) visitApply(apply); for (auto result : II.getResults()) visitValue(result); } } } /// Populate `indirectApplies` and insert this apply in `valueStorageMap` if /// the call's non-tuple result is returned indirectly. void OpaqueValueVisitor::visitApply(ApplySite applySite) { auto calleeConv = applySite.getSubstCalleeConv(); unsigned calleeArgIdx = applySite.getCalleeArgIndexOfFirstAppliedArg(); for (Operand &operand : applySite.getArgumentOperands()) { if (operand.get()->getType().isObject()) { auto argConv = calleeConv.getSILArgumentConvention(calleeArgIdx); if (argConv.isIndirectConvention()) { pass.indirectApplies.insert(applySite); } } ++calleeArgIdx; } if (applySite.getSubstCalleeType()->hasIndirectFormalResults()) { pass.indirectApplies.insert(applySite); if (!applySite.getType().is()) pass.valueStorageMap.insertValue(cast(applySite)); return; } } /// If `value` is address-only add it to the `valueStorageMap`. void OpaqueValueVisitor::visitValue(SILValue value) { if (value->getType().isObject() && value->getType().isAddressOnly(pass.F->getModule())) { if (pass.valueStorageMap.contains(value)) { assert(isa( pass.valueStorageMap.getStorage(value).storageAddress)); return; } pass.valueStorageMap.insertValue(value); } } //===----------------------------------------------------------------------===// // OpaqueStorageAllocation: Generate alloc_stack and address projections for all // abstract storage locations. //===----------------------------------------------------------------------===// namespace { /// Allocate storage on the stack for every opaque value defined in this /// function in RPO order. If the definition is an argument of this function, /// simply replace the function argument with an address representing the /// caller's storage. /// /// TODO: shrink lifetimes by inserting alloc_stack at the dominance LCA and /// finding the lifetime boundary with a simple backward walk from uses. class OpaqueStorageAllocation { AddressLoweringState &pass; public: explicit OpaqueStorageAllocation(AddressLoweringState &pass) : pass(pass) {} void allocateOpaqueStorage(); protected: void convertIndirectFunctionArgs(); unsigned insertIndirectReturnArgs(); bool canProjectFrom(SingleValueInstruction *innerVal, SILInstruction *composingUse); void allocateForValue(SILValue value, ValueStorage &storage); }; } // end anonymous namespace /// Top-level entry point: allocate storage for all opaque/resilient values. void OpaqueStorageAllocation::allocateOpaqueStorage() { // TODO: I think we need a GenericContextScope for mapTypeIntoContext, but all // tests are currently passing without it. #if 0 auto canFnType = pass.F->getLoweredFunctionType(); // Setup a generic context for argument and result types. swift::Lowering::GenericContextScope scope(pass.F->getModule().Types, canFnType->getGenericSignature()); #endif // Fixup this function's argument types with temporary loads. convertIndirectFunctionArgs(); // Create a new function argument for each indirect result. insertIndirectReturnArgs(); // Populate valueStorageMap. OpaqueValueVisitor(pass).mapValueStorage(); // Create an AllocStack for every opaque value defined in the function. Visit // values in post-order to create storage for aggregates before subobjects. for (auto &valueStorageI : reversed(pass.valueStorageMap)) allocateForValue(valueStorageI.first, valueStorageI.second); } /// Replace each value-typed argument to the current function with an /// address-typed argument by inserting a temporary load instruction. void OpaqueStorageAllocation::convertIndirectFunctionArgs() { // Insert temporary argument loads at the top of the function. SILBuilder argBuilder(pass.F->getEntryBlock()->begin()); argBuilder.setSILConventions( SILModuleConventions::getLoweredAddressConventions()); auto fnConv = pass.F->getConventions(); unsigned argIdx = fnConv.getSILArgIndexOfFirstParam(); for (SILParameterInfo param : pass.F->getLoweredFunctionType()->getParameters()) { if (param.isFormalIndirect() && !fnConv.isSILIndirect(param)) { SILArgument *arg = pass.F->getArgument(argIdx); SILType addrType = arg->getType().getAddressType(); LoadInst *loadArg = argBuilder.createLoad( RegularLocation(const_cast(arg->getDecl())), SILUndef::get(addrType, pass.F->getModule()), LoadOwnershipQualifier::Unqualified); arg->replaceAllUsesWith(loadArg); assert(!pass.valueStorageMap.contains(arg)); arg = arg->getParent()->replaceFunctionArgument( arg->getIndex(), addrType, ValueOwnershipKind::Trivial, arg->getDecl()); loadArg->setOperand(arg); if (addrType.isAddressOnly(pass.F->getModule())) pass.valueStorageMap.insertValue(loadArg).storageAddress = arg; } ++argIdx; } assert(argIdx == fnConv.getSILArgIndexOfFirstParam() + fnConv.getNumSILArguments()); } /// Insert function arguments for any @out result type. Return the number of /// indirect result arguments added. unsigned OpaqueStorageAllocation::insertIndirectReturnArgs() { auto &ctx = pass.F->getModule().getASTContext(); unsigned argIdx = 0; for (auto resultTy : pass.loweredFnConv.getIndirectSILResultTypes()) { auto bodyResultTy = pass.F->mapTypeIntoContext(resultTy); auto var = new (ctx) ParamDecl(VarDecl::Specifier::InOut, SourceLoc(), SourceLoc(), ctx.getIdentifier("$return_value"), SourceLoc(), ctx.getIdentifier("$return_value"), bodyResultTy.getSwiftRValueType(), pass.F->getDeclContext()); pass.F->begin()->insertFunctionArgument(argIdx, bodyResultTy.getAddressType(), ValueOwnershipKind::Trivial, var); ++argIdx; } assert(argIdx == pass.loweredFnConv.getNumIndirectSILResults()); return argIdx; } /// Is this operand composing an aggregate from a subobject, or simply /// forwarding the operand's value to storage defined elsewhere? /// /// TODO: Handle struct. /// TODO: Make this a visitor. bool OpaqueStorageAllocation::canProjectFrom(SingleValueInstruction *innerVal, SILInstruction *composingUse) { if (!OptimizeOpaqueAddressLowering) return false; SILValue composingValue; switch (composingUse->getKind()) { default: return false; case SILInstructionKind::ApplyInst: // @in operands never need their own storage since they are non-mutating // uses. They simply reuse the storage allocated for their operand. So it // wouldn't make sense to "project" out of the apply argument. return false; case SILInstructionKind::EnumInst: composingValue = cast(composingUse); break; case SILInstructionKind::InitExistentialValueInst: { // Ensure that all opened archetypes are available at the inner value's // definition. auto *initExistential = cast(composingUse); for (Operand &operand : initExistential->getTypeDependentOperands()) { if (!pass.domInfo->properlyDominates(operand.get(), innerVal)) return false; } composingValue = initExistential; break; } case SILInstructionKind::ReturnInst: return true; case SILInstructionKind::StoreInst: { if (cast(composingUse)->getSrc() == innerVal && isa(innerVal)) { return true; } return false; } case SILInstructionKind::TupleInst: composingValue = cast(composingUse); break; } ValueStorage &storage = pass.valueStorageMap.getStorage(composingValue); if (SILValue addr = storage.storageAddress) { if (auto *stackInst = dyn_cast(addr)) { assert(pass.domInfo->properlyDominates(stackInst, innerVal)); return true; } if (isa(addr)) { return true; } } else if (storage.isProjection()) return canProjectFrom(innerVal, storage.getComposedOperand()->getUser()); return false; } /// Allocate storage for a single opaque/resilient value. void OpaqueStorageAllocation::allocateForValue(SILValue value, ValueStorage &storage) { assert(!isa(value)); if (auto apply = ApplySite::isa(value)) { // Result tuples will be canonicalized during apply rewriting so the tuple // itself is unused. if (value->getType().is()) { assert(apply.getSubstCalleeType()->getNumResults() > 1); return; } } // Argument loads already have a storage address. if (storage.storageAddress) { assert(isa(storage.storageAddress)); return; } if (value->hasOneUse()) { // TODO: Handle block arguments. // TODO: Handle subobjects with a single composition, and other non-mutating // uses such as @in arguments. if (auto *def = dyn_cast(value)) { Operand *useOper = *value->use_begin(); if (canProjectFrom(def, useOper->getUser())) { storage.setComposedOperand(useOper); return; } } } SILBuilder allocBuilder(pass.F->begin()->begin()); allocBuilder.setSILConventions( SILModuleConventions::getLoweredAddressConventions()); AllocStackInst *allocInstr = allocBuilder.createAllocStack(value.getLoc(), value->getType()); storage.storageAddress = allocInstr; // Insert stack deallocations. for (TermInst *termInst : pass.returnInsts) { SILBuilder deallocBuilder(termInst); deallocBuilder.setSILConventions( SILModuleConventions::getLoweredAddressConventions()); deallocBuilder.createDeallocStack(allocInstr->getLoc(), allocInstr); } } //===----------------------------------------------------------------------===// // AddressMaterialization - materialize storage addresses, generate projections. //===----------------------------------------------------------------------===// namespace { /// Materialize the address of a value's storage. For values that are directly /// mapped to a storage location, simply return the mapped `AllocStackInst`. /// For subobjects emit any necessary `_addr` projections using the provided /// `SILBuilder`. /// /// This is a common utility for ApplyRewriter, AddressOnlyDefRewriter, /// and AddressOnlyUseRewriter. class AddressMaterialization { AddressLoweringState &pass; SILBuilder &B; public: AddressMaterialization(AddressLoweringState &pass, SILBuilder &B) : pass(pass), B(B) {} SILValue initializeOperandMem(Operand *operand); SILValue materializeAddress(SILValue origValue); protected: SILValue materializeProjection(Operand *operand); }; } // anonymous namespace // Materialize an address pointing to initialized memory for this operand, // generating a projection and copy if needed. SILValue AddressMaterialization::initializeOperandMem(Operand *operand) { SILValue def = operand->get(); SILValue destAddr; if (operand->get()->getType().isAddressOnly(pass.F->getModule())) { ValueStorage &storage = pass.valueStorageMap.getStorage(def); // Source value should already be rewritten. assert(storage.isRewritten()); if (storage.isProjection()) destAddr = storage.storageAddress; else { destAddr = materializeProjection(operand); B.createCopyAddr(operand->getUser()->getLoc(), storage.storageAddress, destAddr, IsTake, IsInitialization); } } else { destAddr = materializeProjection(operand); B.createStore(operand->getUser()->getLoc(), operand->get(), destAddr, StoreOwnershipQualifier::Unqualified); } return destAddr; } /// Return the address of the storage for `origValue`. This may involve /// materializing projections. SILValue AddressMaterialization::materializeAddress(SILValue origValue) { ValueStorage &storage = pass.valueStorageMap.getStorage(origValue); if (!storage.storageAddress) storage.storageAddress = materializeProjection(storage.getComposedOperand()); return storage.storageAddress; } SILValue AddressMaterialization::materializeProjection(Operand *operand) { SILInstruction *user = operand->getUser(); switch (user->getKind()) { default: DEBUG(user->dump()); llvm_unreachable("Unexpected subobject composition."); case SILInstructionKind::EnumInst: { auto *enumInst = cast(user); SILValue enumAddr = materializeAddress(enumInst); return B.createInitEnumDataAddr(enumInst->getLoc(), enumAddr, enumInst->getElement(), operand->get()->getType().getAddressType()); } case SILInstructionKind::InitExistentialValueInst: { auto *initExistentialValue = cast(user); SILValue containerAddr = materializeAddress(initExistentialValue); auto canTy = initExistentialValue->getFormalConcreteType(); auto opaque = Lowering::AbstractionPattern::getOpaque(); auto &concreteTL = pass.F->getModule().Types.getTypeLowering(opaque, canTy); return B.createInitExistentialAddr( initExistentialValue->getLoc(), containerAddr, canTy, concreteTL.getLoweredType(), initExistentialValue->getConformances()); } case SILInstructionKind::ReturnInst: { assert(pass.loweredFnConv.hasIndirectSILResults()); return pass.F->getArguments()[0]; } case SILInstructionKind::TupleInst: { auto *tupleInst = cast(user); // Function return values. if (tupleInst->hasOneUse() && isa(tupleInst->use_begin()->getUser())) { unsigned resultIdx = tupleInst->getElementIndex(operand); assert(resultIdx < pass.loweredFnConv.getNumIndirectSILResults()); // Cannot call getIndirectSILResults here because that API uses the // original function type. return pass.F->getArguments()[resultIdx]; } // TODO: emit tuple_element_addr llvm_unreachable("Unimplemented"); } } } //===----------------------------------------------------------------------===// // ApplyRewriter - rewrite call sites with indirect arguments. //===----------------------------------------------------------------------===// namespace { /// Rewrite an Apply, lowering its indirect SIL arguments. /// /// Replace indirect parameter arguments of this function with address-type /// arguments. /// /// Insert new indirect result arguments for this function to represent the /// caller's storage. class ApplyRewriter { AddressLoweringState &pass; ApplySite apply; SILBuilder argBuilder; /// For now, we assume that the apply site is a normal apply. ApplyInst *getApplyInst() const { return cast(apply); } public: ApplyRewriter(ApplySite origCall, AddressLoweringState &pass) : pass(pass), apply(origCall), argBuilder(origCall.getInstruction()) { argBuilder.setSILConventions( SILModuleConventions::getLoweredAddressConventions()); } void rewriteParameters(); void rewriteIndirectParameter(Operand *operand); void convertApplyWithIndirectResults(); protected: void canonicalizeResults(MutableArrayRef directResultValues, ArrayRef nonCanonicalUses); SILValue materializeIndirectResultAddress( SingleValueInstruction *origDirectResultVal, SILType argTy); }; } // end anonymous namespace /// Rewrite any indirect parameter in place. void ApplyRewriter::rewriteParameters() { // Rewrite all incoming indirect operands. unsigned calleeArgIdx = apply.getCalleeArgIndexOfFirstAppliedArg(); for (Operand &operand : apply.getArgumentOperands()) { if (operand.get()->getType().isObject()) { auto argConv = apply.getSubstCalleeConv().getSILArgumentConvention(calleeArgIdx); if (argConv.isIndirectConvention()) rewriteIndirectParameter(&operand); } ++calleeArgIdx; } } /// Deallocate temporary call-site stack storage. /// /// `argLoad` is non-null for @out args that are loaded. static void insertStackDeallocationAtCall(AllocStackInst *allocInst, SILInstruction *applyInst, SILInstruction *argLoad) { SILInstruction *lastUse = argLoad ? argLoad : applyInst; switch (applyInst->getKind()) { case SILInstructionKind::ApplyInst: { SILBuilder deallocBuilder(&*std::next(lastUse->getIterator())); deallocBuilder.setSILConventions( SILModuleConventions::getLoweredAddressConventions()); deallocBuilder.createDeallocStack(allocInst->getLoc(), allocInst); break; } case SILInstructionKind::TryApplyInst: // TODO!!!: insert dealloc in the catch block. llvm_unreachable("not implemented for this instruction!"); case SILInstructionKind::PartialApplyInst: llvm_unreachable("partial apply cannot have indirect results."); default: llvm_unreachable("not implemented for this instruction!"); } } /// Rewrite a formally indirect parameter in place. /// Update the operand to the incoming value's storage address. /// After this, the SIL argument types no longer match SIL function conventions. /// /// Temporary argument storage may be created for loadable values. /// /// Note: Temporary argument storage does not own its value. If the argument /// is owned, the stored value should already have been copied. void ApplyRewriter::rewriteIndirectParameter(Operand *operand) { SILValue argValue = operand->get(); if (argValue->getType().isAddressOnly(pass.F->getModule())) { ValueStorage &storage = pass.valueStorageMap.getStorage(argValue); // Source value should already be rewritten. assert(storage.isRewritten()); operand->set(storage.storageAddress); return; } // Allocate temporary storage for a loadable operand. AllocStackInst *allocInstr = argBuilder.createAllocStack(apply.getLoc(), argValue->getType()); argBuilder.createStore(apply.getLoc(), argValue, allocInstr, StoreOwnershipQualifier::Unqualified); operand->set(allocInstr); insertStackDeallocationAtCall(allocInstr, apply.getInstruction(), /*argLoad=*/nullptr); } // Canonicalize call result uses. Treat each result of a multi-result call as // an independent value. Currently, SILGen may generate tuple_extract for each // result but generate a single destroy_value for the entire tuple of // results. This makes it impossible to reason about each call result as an // independent value according to the callee's function type. // // directResultValues has an entry for each tuple extract corresponding to // that result if one exists. This function will add an entry to // directResultValues whenever it needs to materialize a TupleExtractInst. void ApplyRewriter::canonicalizeResults( MutableArrayRef directResultValues, ArrayRef nonCanonicalUses) { auto *applyInst = getApplyInst(); for (Operand *operand : nonCanonicalUses) { auto *destroyInst = dyn_cast(operand->getUser()); if (!destroyInst) llvm::report_fatal_error("Simultaneous use of multiple call results."); for (unsigned resultIdx : indices(directResultValues)) { SingleValueInstruction *result = directResultValues[resultIdx]; if (!result) { SILBuilder resultBuilder(std::next(SILBasicBlock::iterator(applyInst))); resultBuilder.setSILConventions( SILModuleConventions::getLoweredAddressConventions()); result = resultBuilder.createTupleExtract(applyInst->getLoc(), applyInst, resultIdx); directResultValues[resultIdx] = result; } SILBuilder B(destroyInst); B.setSILConventions(SILModuleConventions::getLoweredAddressConventions()); auto &TL = pass.F->getModule().getTypeLowering(result->getType()); TL.emitDestroyValue(B, destroyInst->getLoc(), result); } destroyInst->eraseFromParent(); } } /// Return the storage address for the indirect result corresponding to the /// given original result value. Allocate temporary argument storage for any /// indirect results that are unmapped because they are loadable or unused. /// /// origDirectResultVal may be nullptr for unused results. SILValue ApplyRewriter::materializeIndirectResultAddress( SingleValueInstruction *origDirectResultVal, SILType argTy) { if (origDirectResultVal && origDirectResultVal->getType().isAddressOnly(pass.F->getModule())) { auto &storage = pass.valueStorageMap.getStorage(origDirectResultVal); storage.markRewritten(); // Pass the local storage address as the indirect result address. return storage.storageAddress; } // Allocate temporary call-site storage for an unused or loadable result. SILInstruction *origCallInst = apply.getInstruction(); SILLocation loc = origCallInst->getLoc(); auto *allocInst = argBuilder.createAllocStack(loc, argTy); LoadInst *loadInst = nullptr; if (origDirectResultVal) { // TODO: Find the try_apply's result block. // Build results outside-in to next stack allocations. SILBuilder resultBuilder(std::next(SILBasicBlock::iterator(origCallInst))); resultBuilder.setSILConventions( SILModuleConventions::getLoweredAddressConventions()); // This is a formally indirect argument, but is loadable. loadInst = resultBuilder.createLoad(loc, allocInst, LoadOwnershipQualifier::Unqualified); origDirectResultVal->replaceAllUsesWith(loadInst); pass.markDead(origDirectResultVal); } insertStackDeallocationAtCall(allocInst, origCallInst, loadInst); return SILValue(allocInst); } /// Allocate storage for formally indirect results at the given call site. /// Create a new call instruction with indirect SIL arguments. void ApplyRewriter::convertApplyWithIndirectResults() { assert(apply.getSubstCalleeType()->hasIndirectFormalResults()); auto *origCallInst = getApplyInst(); SILFunctionConventions origFnConv = apply.getSubstCalleeConv(); // Gather the original direct return values. // Canonicalize results so no user uses more than one result. SmallVector origDirectResultValues( origFnConv.getNumDirectSILResults()); SmallVector nonCanonicalUses; if (origCallInst->getType().is()) { for (Operand *operand : origCallInst->getUses()) { if (auto *extract = dyn_cast(operand->getUser())) origDirectResultValues[extract->getFieldNo()] = extract; else nonCanonicalUses.push_back(operand); } if (!nonCanonicalUses.empty()) canonicalizeResults(origDirectResultValues, nonCanonicalUses); } else { // This call has a single, indirect result (convertApplyWithIndirectResults // only handles call with at least one indirect result). // An unused result can remain unmapped. Temporary storage will be allocated // later when fixing up the call's uses. assert(origDirectResultValues.size() == 1); if (!origCallInst->use_empty()) { assert(pass.valueStorageMap.contains(origCallInst)); origDirectResultValues[0] = origCallInst; } } // Prepare to emit a new call instruction. SILLocation loc = origCallInst->getLoc(); SILBuilder callBuilder(origCallInst); callBuilder.setSILConventions( SILModuleConventions::getLoweredAddressConventions()); // The new call instruction's SIL calling convention. SILFunctionConventions loweredCalleeConv( apply.getSubstCalleeType(), SILModuleConventions::getLoweredAddressConventions()); // The new call instruction's SIL argument list. SmallVector newCallArgs(loweredCalleeConv.getNumSILArguments()); // Map the original result indices to new result indices. SmallVector newDirectResultIndices( origFnConv.getNumDirectSILResults()); // Indices used to populate newDirectResultIndices. unsigned oldDirectResultIdx = 0, newDirectResultIdx = 0; // The index of the next indirect result argument. unsigned newResultArgIdx = loweredCalleeConv.getSILArgIndexOfFirstIndirectResult(); // Visit each result. Redirect results that are now indirect by calling // materializeIndirectResultAddress. Result that remain direct will be // redirected later. Populate newCallArgs and newDirectResultIndices. for_each( apply.getSubstCalleeType()->getResults(), origDirectResultValues, [&](SILResultInfo resultInfo, SingleValueInstruction *origDirectResultVal) { // Assume that all original results are direct in SIL. assert(!origFnConv.isSILIndirect(resultInfo)); if (loweredCalleeConv.isSILIndirect(resultInfo)) { SILValue indirectResultAddr = materializeIndirectResultAddress( origDirectResultVal, loweredCalleeConv.getSILType(resultInfo)); // Record the new indirect call argument. newCallArgs[newResultArgIdx++] = indirectResultAddr; // Leave a placeholder for indirect results. newDirectResultIndices[oldDirectResultIdx++] = ~0; } else { // Record the new direct result, and advance the direct result indices. newDirectResultIndices[oldDirectResultIdx++] = newDirectResultIdx++; } // replaceAllUses will be called later to handle direct results that // remain direct results of the new call instruction. }); // Append the existing call arguments to the SIL argument list. They were // already lowered to addresses by rewriteIncomingArgument. assert(newResultArgIdx == loweredCalleeConv.getSILArgIndexOfFirstParam()); unsigned origArgIdx = apply.getSubstCalleeConv().getSILArgIndexOfFirstParam(); for (unsigned endIdx = newCallArgs.size(); newResultArgIdx < endIdx; ++newResultArgIdx, ++origArgIdx) { newCallArgs[newResultArgIdx] = apply.getArgument(origArgIdx); } // Create a new apply with indirect result operands. ApplyInst *newCallInst; switch (origCallInst->getKind()) { case SILInstructionKind::ApplyInst: newCallInst = callBuilder.createApply( loc, apply.getCallee(), apply.getSubstitutions(), newCallArgs, cast(origCallInst)->isNonThrowing(), nullptr); break; case SILInstructionKind::TryApplyInst: // TODO: insert dealloc in the catch block. llvm_unreachable("not implemented for this instruction!"); case SILInstructionKind::PartialApplyInst: // Partial apply does not have formally indirect results. default: llvm_unreachable("not implemented for this instruction!"); } // Replace all unmapped uses of the original call with uses of the new call. // // TODO: handle bbargs from try_apply. SILBuilder resultBuilder( std::next(SILBasicBlock::iterator(origCallInst))); resultBuilder.setSILConventions( SILModuleConventions::getLoweredAddressConventions()); SmallVector origUses(origCallInst->getUses()); for (Operand *operand : origUses) { auto *extractInst = dyn_cast(operand->getUser()); if (!extractInst) { assert(origFnConv.getNumDirectSILResults() == 1); assert(pass.valueStorageMap.contains(origCallInst)); continue; } unsigned origResultIdx = extractInst->getFieldNo(); auto resultInfo = origFnConv.getResults()[origResultIdx]; if (extractInst->getType().isAddressOnly(pass.F->getModule())) { // Uses of indirect results will be rewritten by AddressOnlyUseRewriter. assert(loweredCalleeConv.isSILIndirect(resultInfo)); assert(pass.valueStorageMap.contains(extractInst)); if (extractInst->use_empty()) pass.markDead(extractInst); continue; } if (loweredCalleeConv.isSILIndirect(resultInfo)) { // This loadable indirect use should already be redirected to a load from // the argument storage and marked dead. assert(extractInst->use_empty()); continue; } // Either the new call instruction has only a single direct result, or we // map the original tuple field to the new tuple field. SILValue newValue = newCallInst; if (loweredCalleeConv.getNumDirectSILResults() > 1) { assert(newValue->getType().is()); newValue = resultBuilder.createTupleExtract( extractInst->getLoc(), newValue, newDirectResultIndices[origResultIdx]); } extractInst->replaceAllUsesWith(newValue); extractInst->eraseFromParent(); } if (!pass.valueStorageMap.contains(origCallInst)) pass.markDead(origCallInst); } //===----------------------------------------------------------------------===// // ReturnRewriter - rewrite return instructions for indirect results. //===----------------------------------------------------------------------===// class ReturnRewriter { AddressLoweringState &pass; public: ReturnRewriter(AddressLoweringState &pass) : pass(pass) {} void rewriteReturns(); protected: void rewriteReturn(ReturnInst *returnInst); }; void ReturnRewriter::rewriteReturns() { for (TermInst *termInst : pass.returnInsts) { // TODO: handle throws rewriteReturn(cast(termInst)); } } void ReturnRewriter::rewriteReturn(ReturnInst *returnInst) { auto insertPt = SILBasicBlock::iterator(returnInst); auto bbStart = returnInst->getParent()->begin(); while (insertPt != bbStart) { --insertPt; if (!isa(*insertPt)) break; } SILBuilder B(insertPt); B.setSILConventions(SILModuleConventions::getLoweredAddressConventions()); // Gather direct function results. unsigned numOrigDirectResults = pass.F->getConventions().getNumDirectSILResults(); SmallVector origDirectResultValues; if (numOrigDirectResults == 1) origDirectResultValues.push_back(returnInst->getOperand()); else { auto *tupleInst = cast(returnInst->getOperand()); origDirectResultValues.append(tupleInst->getElements().begin(), tupleInst->getElements().end()); assert(origDirectResultValues.size() == numOrigDirectResults); } SILFunctionConventions origFnConv(pass.F->getConventions()); (void)origFnConv; // Convert each result. SmallVector newDirectResults; unsigned newResultArgIdx = pass.loweredFnConv.getSILArgIndexOfFirstIndirectResult(); for_each( pass.F->getLoweredFunctionType()->getResults(), origDirectResultValues, [&](SILResultInfo resultInfo, SILValue origDirectResultVal) { // Assume that all original results are direct in SIL. assert(!origFnConv.isSILIndirect(resultInfo)); if (pass.loweredFnConv.isSILIndirect(resultInfo)) { assert(newResultArgIdx < pass.loweredFnConv.getSILArgIndexOfFirstParam()); SILArgument *resultArg = B.getFunction().getArgument(newResultArgIdx); SILType resultTy = origDirectResultVal->getType(); if (resultTy.isAddressOnly(pass.F->getModule())) { ValueStorage &storage = pass.valueStorageMap.getStorage(origDirectResultVal); assert(storage.isRewritten()); if (!storage.isProjection()) { // Copy the result from local storage into the result argument. SILValue resultAddr = storage.storageAddress; B.createCopyAddr(returnInst->getLoc(), resultAddr, resultArg, IsTake, IsInitialization); } } else { // Store the result into the result argument. B.createStore(returnInst->getLoc(), origDirectResultVal, resultArg, StoreOwnershipQualifier::Unqualified); } ++newResultArgIdx; } else { // Record the direct result for populating the result tuple. newDirectResults.push_back(origDirectResultVal); } }); assert(newDirectResults.size() == pass.loweredFnConv.getNumDirectSILResults()); SILValue newReturnVal; if (newDirectResults.empty()) { SILType emptyTy = B.getModule().Types.getLoweredType( TupleType::getEmpty(B.getModule().getASTContext())); newReturnVal = B.createTuple(returnInst->getLoc(), emptyTy, {}); } else if (newDirectResults.size() == 1) { newReturnVal = newDirectResults[0]; } else { newReturnVal = B.createTuple(returnInst->getLoc(), pass.loweredFnConv.getSILResultType(), newDirectResults); } SILValue origFullResult = returnInst->getOperand(); returnInst->setOperand(newReturnVal); if (auto *fullResultInst = origFullResult->getDefiningInstruction()) { if (!fullResultInst->hasUsesOfAnyResult()) pass.markDead(fullResultInst); } } //===----------------------------------------------------------------------===// // AddressOnlyUseRewriter - rewrite opaque value uses. //===----------------------------------------------------------------------===// namespace { class AddressOnlyUseRewriter : SILInstructionVisitor { friend SILVisitorBase; friend SILInstructionVisitor; AddressLoweringState &pass; SILBuilder B; AddressMaterialization addrMat; Operand *currOper; public: explicit AddressOnlyUseRewriter(AddressLoweringState &pass) : pass(pass), B(*pass.F), addrMat(pass, B) { B.setSILConventions(SILModuleConventions::getLoweredAddressConventions()); } void visitOperand(Operand *operand) { currOper = operand; visit(operand->getUser()); } protected: void markRewritten(SILValue oldValue, SILValue addr) { auto &storage = pass.valueStorageMap.getStorage(oldValue); storage.storageAddress = addr; storage.markRewritten(); } void beforeVisit(SILInstruction *I) { DEBUG(llvm::dbgs() << " REWRITE USE "; I->dump()); B.setInsertionPoint(I); B.setCurrentDebugScope(I->getDebugScope()); } void visitSILInstruction(SILInstruction *I) { DEBUG(I->dump()); llvm_unreachable("Unimplemented?!"); } void visitApplyInst(ApplyInst *applyInst) { ApplyRewriter(applyInst, pass).rewriteIndirectParameter(currOper); } void visitCopyValueInst(CopyValueInst *copyInst) { ValueStorage &storage = pass.valueStorageMap.getStorage(copyInst); // Fold a copy into a store. if (storage.isProjection() && isa(storage.getComposedOperand()->getUser())) { return; } SILValue srcVal = copyInst->getOperand(); SILValue srcAddr = pass.valueStorageMap.getStorage(srcVal).storageAddress; SILValue destAddr = addrMat.materializeAddress(copyInst); B.createCopyAddr(copyInst->getLoc(), srcAddr, destAddr, IsNotTake, IsInitialization); markRewritten(copyInst, destAddr); } void visitDebugValueInst(DebugValueInst *debugInst) { SILValue srcVal = debugInst->getOperand(); SILValue srcAddr = pass.valueStorageMap.getStorage(srcVal).storageAddress; B.createDebugValueAddr(debugInst->getLoc(), srcAddr); pass.markDead(debugInst); } void visitDestroyValueInst(DestroyValueInst *destroyInst) { SILValue srcVal = destroyInst->getOperand(); SILValue srcAddr = pass.valueStorageMap.getStorage(srcVal).storageAddress; B.createDestroyAddr(destroyInst->getLoc(), srcAddr); pass.markDead(destroyInst); } // Handle EnumInst on the def side to handle both opaque and // loadable operands. void visitEnumInst(EnumInst *enumInst) {} // Handle InitExistentialValue on the def side to handle both opaque and // loadable operands. void visitInitExistentialValueInst(InitExistentialValueInst *initExistential) {} void visitReturnInst(ReturnInst *returnInst) { // Returns are rewritten for any function with indirect results after opaque // value rewriting. } void visitStoreInst(StoreInst *storeInst) { SILValue srcVal = storeInst->getSrc(); assert(currOper->get() == srcVal); ValueStorage &storage = pass.valueStorageMap.getStorage(srcVal); SILValue srcAddr = storage.storageAddress; IsTake_t isTakeFlag = IsTake; assert(storeInst->getOwnershipQualifier() == StoreOwnershipQualifier::Unqualified); if (storage.isProjection()) { assert(!srcAddr); auto *copyInst = cast(srcVal); ValueStorage &srcStorage = pass.valueStorageMap.getStorage(copyInst->getOperand()); assert(!srcStorage.isProjection()); srcAddr = srcStorage.storageAddress; isTakeFlag = IsNotTake; } // Bitwise copy the value. Two locations now share ownership. This is // modeled as a take-init. B.createCopyAddr(storeInst->getLoc(), srcAddr, storeInst->getDest(), isTakeFlag, IsInitialization); pass.markDead(storeInst); } void visitTupleInst(TupleInst *tupleInst) { // Tuples are rewritten on the def-side, where both direct and indirect // elements are composed. } void visitTupleExtractInst(TupleExtractInst *extractInst) { // Apply results are rewritten when the result definition is visited. if (ApplySite::isa(currOper->get())) return; // TODO: generate tuple_element_addr. // generate copy_addr if we can't project. llvm_unreachable("unimplemented."); } }; } // end anonymous namespace //===----------------------------------------------------------------------===// // AddressOnlyDefRewriter - rewrite opaque value definitions. //===----------------------------------------------------------------------===// namespace { class AddressOnlyDefRewriter : SILInstructionVisitor { friend SILVisitorBase; friend SILInstructionVisitor; AddressLoweringState &pass; SILBuilder B; AddressMaterialization addrMat; ValueStorage *storage = nullptr; public: explicit AddressOnlyDefRewriter(AddressLoweringState &pass) : pass(pass), B(*pass.F), addrMat(pass, B) { B.setSILConventions(SILModuleConventions::getLoweredAddressConventions()); } void visitInst(SILInstruction *inst) { visit(inst); } protected: void beforeVisit(SILInstruction *I) { // This cast succeeds beecause only specific instructions get added to // the value storage map. storage = &pass.valueStorageMap.getStorage(cast(I)); DEBUG(llvm::dbgs() << "REWRITE DEF "; I->dump()); if (storage->storageAddress) DEBUG(llvm::dbgs() << " STORAGE "; storage->storageAddress->dump()); B.setInsertionPoint(I); B.setCurrentDebugScope(I->getDebugScope()); } void visitSILInstruction(SILInstruction *I) { DEBUG(I->dump()); llvm_unreachable("Unimplemented?!"); } void visitApplyInst(ApplyInst *applyInst) { assert(isa(applyInst) && "beforeVisit assumes that ApplyInst is an SVI"); assert(!storage->isRewritten()); // Completely rewrite the apply instruction, handling any remaining // (loadable) indirect parameters, allocating memory for indirect // results, and generating a new apply instruction. ApplyRewriter rewriter(applyInst, pass); rewriter.rewriteParameters(); rewriter.convertApplyWithIndirectResults(); } void visitCopyValueInst(CopyValueInst *copyInst) { // A folded copy is not rewritten. assert(storage->isProjection() || storage->isRewritten()); } void visitEnumInst(EnumInst *enumInst) { SILValue enumAddr; if (enumInst->hasOperand()) { addrMat.initializeOperandMem(&enumInst->getOperandRef()); assert(storage->storageAddress); enumAddr = storage->storageAddress; } else enumAddr = addrMat.materializeAddress(enumInst); B.createInjectEnumAddr(enumInst->getLoc(), enumAddr, enumInst->getElement()); storage->markRewritten(); } void visitInitExistentialValueInst( InitExistentialValueInst *initExistentialValue) { // Initialize memory for the operand which may be opaque or loadable. addrMat.initializeOperandMem(&initExistentialValue->getOperandRef()); assert(storage->storageAddress); storage->markRewritten(); } void visitLoadInst(LoadInst *loadInst) { // Bitwise copy the value. Two locations now share ownership. This is // modeled as a take-init. SILValue addr = pass.valueStorageMap.getStorage(loadInst).storageAddress; if (addr != loadInst->getOperand()) { B.createCopyAddr(loadInst->getLoc(), loadInst->getOperand(), addr, IsTake, IsInitialization); } storage->markRewritten(); } void visitTupleInst(TupleInst *tupleInst) { ValueStorage &storage = pass.valueStorageMap.getStorage(tupleInst); if (storage.isProjection() && isa(storage.getComposedOperand()->getUser())) { // For indirectly returned values, each element has its own storage. return; } // For each element, initialize the operand's memory. Some tuple elements // may be loadable types. SILValue tupleAddr = addrMat.materializeAddress(tupleInst); unsigned eltIdx = 0; for (Operand &operand : tupleInst->getAllOperands()) { SILType eltTy = operand.get()->getType(); if (eltTy.isAddressOnly(pass.F->getModule())) addrMat.initializeOperandMem(&operand); else { auto *elementAddr = B.createTupleElementAddr( tupleInst->getLoc(), tupleAddr, eltIdx, eltTy.getAddressType()); B.createStore(tupleInst->getLoc(), operand.get(), elementAddr, StoreOwnershipQualifier::Unqualified); } ++eltIdx; } } void visitTupleExtractInst(TupleExtractInst *extractInst) { // If the source is an opaque tuple, as opposed to a call result, then the // extract is rewritten on the use-side. if (storage->isRewritten()) return; // This must be an indirect result for an apply that has not yet been // rewritten. Rewrite the apply. SILValue srcVal = extractInst->getOperand(); ApplyRewriter(cast(srcVal), pass) .convertApplyWithIndirectResults(); assert(storage->storageAddress); } }; } // end anonymous namespace static void rewriteFunction(AddressLoweringState &pass) { AddressOnlyDefRewriter defVisitor(pass); AddressOnlyUseRewriter useVisitor(pass); for (auto &valueStorageI : pass.valueStorageMap) { SILValue valueDef = valueStorageI.first; // TODO: MultiValueInstruction: ApplyInst if (auto *defInst = dyn_cast(valueDef)) defVisitor.visitInst(defInst); SmallVector uses(valueDef->getUses()); for (Operand *oper : uses) useVisitor.visitOperand(oper); } // Rewrite any remaining (loadable) indirect parameters. for (ApplySite apply : pass.indirectApplies) { // Calls with indirect formal results have already been rewritten. if (apply.getSubstCalleeType()->hasIndirectFormalResults()) { bool isRewritten = false; visitCallResults(apply, [&](SILValue result) { if (result->getType().isAddressOnly(pass.F->getModule())) { assert(pass.valueStorageMap.getStorage(result).isRewritten()); isRewritten = true; return false; } return true; }); if (!isRewritten) { ApplyRewriter rewriter(apply, pass); rewriter.rewriteParameters(); rewriter.convertApplyWithIndirectResults(); continue; } } ApplyRewriter(apply, pass).rewriteParameters(); } if (pass.F->getLoweredFunctionType()->hasIndirectFormalResults()) ReturnRewriter(pass).rewriteReturns(); } //===----------------------------------------------------------------------===// // AddressLowering: Top-Level Function Transform. //===----------------------------------------------------------------------===// namespace { class AddressLowering : public SILModuleTransform { /// The entry point to this function transformation. void run() override; void runOnFunction(SILFunction *F); }; } // end anonymous namespace void AddressLowering::runOnFunction(SILFunction *F) { auto *DA = PM->getAnalysis(); AddressLoweringState pass(F, DA->get(F)); // Rewrite function args and insert alloc_stack/dealloc_stack. OpaqueStorageAllocation allocator(pass); allocator.allocateOpaqueStorage(); DEBUG(llvm::dbgs() << "\nREWRITING: " << F->getName(); F->dump()); // Rewrite instructions with address-only operands or results. rewriteFunction(pass); invalidateAnalysis(F, SILAnalysis::InvalidationKind::Instructions); // Instructions that were explicitly marked dead should already have no // users. // // Add the rest of the instructions to the dead list in post order. // FIXME: make sure we cleaned up address-only BB arguments. for (auto &valueStorageI : reversed(pass.valueStorageMap)) { // TODO: MultiValueInstruction: ApplyInst auto *deadInst = dyn_cast(valueStorageI.first); if (!deadInst) continue; DEBUG(llvm::dbgs() << "DEAD "; deadInst->dump()); #ifndef NDEBUG for (auto result : deadInst->getResults()) for (Operand *operand : result->getUses()) assert(pass.instsToDelete.count(operand->getUser())); #endif pass.instsToDelete.insert(deadInst); } pass.valueStorageMap.clear(); // Delete instructions in postorder recursivelyDeleteTriviallyDeadInstructions(pass.instsToDelete.takeVector(), true); } /// The entry point to this function transformation. void AddressLowering::run() { if (getModule()->getASTContext().LangOpts.EnableSILOpaqueValues) { for (auto &F : *getModule()) runOnFunction(&F); } // Set the SIL state before the PassManager has a chance to run // verification. getModule()->setStage(SILStage::Lowered); } SILTransform *swift::createAddressLowering() { return new AddressLowering(); }