//===--- LoadableByAddress.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 loadable SILTypes. On completion, the SILType of every // function argument is an address instead of the type itself. // This reduces the code size. // Consequently, this pass is required for IRGen. // It is a mandatory IRGen preparation pass (not a diagnostic pass). // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "loadable-address" #include "Explosion.h" #include "FixedTypeInfo.h" #include "IRGenMangler.h" #include "IRGenModule.h" #include "NativeConventionSchema.h" #include "swift/AST/GenericEnvironment.h" #include "swift/Basic/Assertions.h" #include "swift/IRGen/IRGenSILPasses.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILCloner.h" #include "swift/SIL/SILUndef.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" #include "swift/SILOptimizer/Utils/DebugOptUtils.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/StackNesting.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; using namespace swift::irgen; static GenericEnvironment *getSubstGenericEnvironment(CanSILFunctionType fnTy) { return fnTy->getSubstGenericSignature().getGenericEnvironment(); } static GenericEnvironment *getSubstGenericEnvironment(SILFunction *F) { if (F->getLoweredFunctionType()->isPolymorphic()) return F->getGenericEnvironment(); return getSubstGenericEnvironment(F->getLoweredFunctionType()); } class LargeSILTypeMapper { public: LargeSILTypeMapper() {} public: SILType getNewSILType(GenericEnvironment *GenericEnv, SILType storageType, irgen::IRGenModule &Mod); bool shouldTransformResults(GenericEnvironment *env, CanSILFunctionType fnType, irgen::IRGenModule &IGM); bool shouldTransformFunctionType(GenericEnvironment *env, CanSILFunctionType fnType, irgen::IRGenModule &IGM); SILParameterInfo getNewParameter(GenericEnvironment *env, SILParameterInfo param, irgen::IRGenModule &IGM); bool shouldTransformParameter(GenericEnvironment *env, SILParameterInfo param, irgen::IRGenModule &IGM); SmallVector getNewParameters(GenericEnvironment *env, CanSILFunctionType fnType, irgen::IRGenModule &IGM); SmallVector getNewYields(GenericEnvironment *env, CanSILFunctionType fnType, irgen::IRGenModule &IGM); SmallVector getNewResults(GenericEnvironment *GenericEnv, CanSILFunctionType fnType, irgen::IRGenModule &Mod, bool mustTransform = false); CanSILFunctionType getNewSILFunctionType(GenericEnvironment *env, CanSILFunctionType fnType, irgen::IRGenModule &IGM, bool mustTransform = false); SILType getNewOptionalFunctionType(GenericEnvironment *GenericEnv, SILType storageType, irgen::IRGenModule &Mod); SILType getNewTupleType(GenericEnvironment *GenericEnv, irgen::IRGenModule &Mod, const SILType &nonOptionalType, const SILType &storageType); bool newResultsDiffer(GenericEnvironment *GenericEnv, ArrayRef origResults, irgen::IRGenModule &Mod); bool shouldConvertBBArg(SILArgument *arg, irgen::IRGenModule &Mod); bool containsDifferentFunctionSignature(GenericEnvironment *genEnv, irgen::IRGenModule &Mod, SILType storageType, SILType newSILType); private: // Cache of already computed type transforms llvm::MapVector, SILType> oldToNewTypeMap; }; /// Utility to determine if this is a large loadable type static bool isLargeLoadableType(GenericEnvironment *GenericEnv, SILType t, irgen::IRGenModule &Mod) { if (t.isAddress() || t.isClassOrClassMetatype()) { return false; } auto canType = t.getASTType(); if (canType->hasTypeParameter()) { assert(GenericEnv && "Expected a GenericEnv"); canType = GenericEnv->mapTypeIntoContext(canType)->getCanonicalType(); } if (canType.getAnyGeneric() || t.is()) { assert(t.isObject() && "Expected only two categories: address and object"); assert(!canType->hasTypeParameter()); const TypeInfo &TI = Mod.getTypeInfoForLowered(canType); auto &nativeSchemaOrigParam = TI.nativeParameterValueSchema(Mod); return nativeSchemaOrigParam.requiresIndirect(); } return false; } static bool modifiableFunction(CanSILFunctionType funcType) { if (funcType->getLanguage() == SILFunctionLanguage::C) { // C functions should use the old ABI return false; } return true; } bool LargeSILTypeMapper::shouldTransformParameter(GenericEnvironment *env, SILParameterInfo param, irgen::IRGenModule &IGM) { auto newParam = getNewParameter(env, param, IGM); return (param != newParam); } static bool isFuncOrOptionalFuncType(SILType Ty) { SILType nonOptionalType = Ty; if (auto optType = Ty.getOptionalObjectType()) { nonOptionalType = optType; } return nonOptionalType.is(); } bool LargeSILTypeMapper::shouldTransformFunctionType(GenericEnvironment *env, CanSILFunctionType fnType, irgen::IRGenModule &IGM) { // Map substituted function types according to their substituted generic // signature. if (fnType->getPatternSubstitutions()) { env = getSubstGenericEnvironment(fnType); } if (shouldTransformResults(env, fnType, IGM)) return true; for (auto param : fnType->getParameters()) { if (shouldTransformParameter(env, param, IGM)) return true; } for (auto yield : fnType->getYields()) { if (shouldTransformParameter(env, yield, IGM)) return true; } return false; } // Get the function type or the optional function type static CanSILFunctionType getInnerFunctionType(SILType storageType) { if (auto currSILFunctionType = storageType.getAs()) { return currSILFunctionType; } if (auto optionalType = storageType.getOptionalObjectType()) { if (auto currSILFunctionType = optionalType.getAs()) { return currSILFunctionType; } } return CanSILFunctionType(); } static SILType getNonOptionalType(SILType t) { SILType nonOptionalType = t; if (auto optType = t.getOptionalObjectType()) { nonOptionalType = optType; } return nonOptionalType; } bool LargeSILTypeMapper::containsDifferentFunctionSignature( GenericEnvironment *genEnv, irgen::IRGenModule &Mod, SILType storageType, SILType newSILType) { if (storageType == newSILType) { return false; } if (getInnerFunctionType(storageType)) { return true; } SILType nonOptionalType = getNonOptionalType(storageType); if (nonOptionalType.getAs()) { auto origType = nonOptionalType.getAs(); for (TupleTypeElt canElem : origType->getElements()) { auto origCanType = CanType(canElem.getType()); auto elem = SILType::getPrimitiveObjectType(origCanType); auto newElem = getNewSILType(genEnv, elem, Mod); if (containsDifferentFunctionSignature(genEnv, Mod, elem, newElem)) { return true; } } } return false; } bool LargeSILTypeMapper::newResultsDiffer(GenericEnvironment *GenericEnv, ArrayRef origResults, irgen::IRGenModule &Mod) { SmallVector newResults; for (auto result : origResults) { SILType currResultTy = result.getSILStorageInterfaceType(); SILType newSILType = getNewSILType(GenericEnv, currResultTy, Mod); // We (currently) only care about function signatures if (containsDifferentFunctionSignature(GenericEnv, Mod, currResultTy, newSILType)) { return true; } } return false; } static bool modNonFuncTypeResultType(GenericEnvironment *genEnv, CanSILFunctionType loweredTy, irgen::IRGenModule &Mod, bool mustTransform = false) { if (!modifiableFunction(loweredTy) && !mustTransform) { return false; } if (loweredTy->getNumResults() != 1) { return false; } auto singleResult = loweredTy->getSingleResult(); auto resultStorageType = singleResult.getSILStorageInterfaceType(); if (isLargeLoadableType(genEnv, resultStorageType, Mod)) { return true; } return false; } SmallVector LargeSILTypeMapper::getNewResults(GenericEnvironment *GenericEnv, CanSILFunctionType fnType, irgen::IRGenModule &Mod, bool mustTransform) { // Get new SIL Function results - same as old results UNLESS: // 1) Function type results might have a different signature // 2) Large loadables are replaced by @out version auto origResults = fnType->getResults(); SmallVector newResults; for (auto result : origResults) { SILType currResultTy = result.getSILStorageInterfaceType(); SILType newSILType = getNewSILType(GenericEnv, currResultTy, Mod); if (modNonFuncTypeResultType(GenericEnv, fnType, Mod, mustTransform)) { // Case (2) Above SILResultInfo newSILResultInfo(newSILType.getASTType(), ResultConvention::Indirect); newResults.push_back(newSILResultInfo); } else if (containsDifferentFunctionSignature(GenericEnv, Mod, currResultTy, newSILType)) { // Case (1) Above SILResultInfo newResult(newSILType.getASTType(), result.getConvention()); newResults.push_back(newResult); } else { newResults.push_back(result); } } return newResults; } CanSILFunctionType LargeSILTypeMapper::getNewSILFunctionType(GenericEnvironment *env, CanSILFunctionType fnType, irgen::IRGenModule &IGM, bool mustTransform) { if (!modifiableFunction(fnType) && !mustTransform) { return fnType; } // Map substituted function types according to their substituted generic // signature. if (fnType->getPatternSubstitutions()) { env = getSubstGenericEnvironment(fnType); } auto newParams = getNewParameters(env, fnType, IGM); auto newYields = getNewYields(env, fnType, IGM); auto newResults = getNewResults(env, fnType, IGM, mustTransform); auto newFnType = SILFunctionType::get( fnType->getInvocationGenericSignature(), fnType->getExtInfo(), fnType->getCoroutineKind(), fnType->getCalleeConvention(), newParams, newYields, newResults, fnType->getOptionalErrorResult(), fnType->getPatternSubstitutions(), fnType->getInvocationSubstitutions(), fnType->getASTContext(), fnType->getWitnessMethodConformanceOrInvalid()); return newFnType; } SILType LargeSILTypeMapper::getNewOptionalFunctionType(GenericEnvironment *GenericEnv, SILType storageType, irgen::IRGenModule &Mod) { SILType newSILType = storageType; if (auto objectType = storageType.getOptionalObjectType()) { if (auto fnType = objectType.getAs()) { if (shouldTransformFunctionType(GenericEnv, fnType, Mod)) { auto newFnType = getNewSILFunctionType(GenericEnv, fnType, Mod); newSILType = SILType::getPrimitiveType(newFnType, storageType.getCategory()); newSILType = SILType::getOptionalType(newSILType); } } } return newSILType; } bool LargeSILTypeMapper::shouldTransformResults(GenericEnvironment *genEnv, CanSILFunctionType loweredTy, irgen::IRGenModule &Mod) { if (!modifiableFunction(loweredTy)) { return false; } if (loweredTy->getNumResults() != 1) { auto resultType = loweredTy->getAllResultsInterfaceType(); auto newResultType = getNewSILType(genEnv, resultType, Mod); return resultType != newResultType; } auto singleResult = loweredTy->getSingleResult(); auto resultStorageType = singleResult.getSILStorageInterfaceType(); auto newResultStorageType = getNewSILType(genEnv, resultStorageType, Mod); if (resultStorageType != newResultStorageType) { return true; } return modNonFuncTypeResultType(genEnv, loweredTy, Mod); } static bool modResultType(SILFunction *F, irgen::IRGenModule &Mod, LargeSILTypeMapper &Mapper) { GenericEnvironment *genEnv = getSubstGenericEnvironment(F); auto loweredTy = F->getLoweredFunctionType(); return Mapper.shouldTransformResults(genEnv, loweredTy, Mod); } static bool shouldTransformYields(GenericEnvironment *genEnv, CanSILFunctionType loweredTy, irgen::IRGenModule &Mod, LargeSILTypeMapper &Mapper, TypeExpansionContext expansion) { if (!modifiableFunction(loweredTy)) { return false; } for (auto &yield : loweredTy->getYields()) { auto yieldStorageType = yield.getSILStorageType(Mod.getSILModule(), loweredTy, expansion); auto newYieldStorageType = Mapper.getNewSILType(genEnv, yieldStorageType, Mod); if (yieldStorageType != newYieldStorageType) return true; } return false; } static bool modYieldType(SILFunction *F, irgen::IRGenModule &Mod, LargeSILTypeMapper &Mapper) { GenericEnvironment *genEnv = getSubstGenericEnvironment(F); auto loweredTy = F->getLoweredFunctionType(); return shouldTransformYields(genEnv, loweredTy, Mod, Mapper, F->getTypeExpansionContext()); } SILParameterInfo LargeSILTypeMapper::getNewParameter(GenericEnvironment *env, SILParameterInfo param, irgen::IRGenModule &IGM) { SILType storageType = param.getSILStorageInterfaceType(); SILType newOptFuncType = getNewOptionalFunctionType(env, storageType, IGM); if (newOptFuncType != storageType) { return param.getWithInterfaceType(newOptFuncType.getASTType()); } if (auto paramFnType = storageType.getAs()) { if (shouldTransformFunctionType(env, paramFnType, IGM)) { auto newFnType = getNewSILFunctionType(env, paramFnType, IGM); return param.getWithInterfaceType(newFnType); } else { return param; } } else if (isLargeLoadableType(env, storageType, IGM)) { if (param.getConvention() == ParameterConvention::Direct_Owned) { return SILParameterInfo(storageType.getASTType(), ParameterConvention::Indirect_In, param.getOptions()); } assert(param.getConvention() == ParameterConvention::Direct_Guaranteed || param.getConvention() == ParameterConvention::Direct_Unowned); return SILParameterInfo(storageType.getASTType(), ParameterConvention::Indirect_In_Guaranteed, param.getOptions()); } else { auto newType = getNewSILType(env, storageType, IGM); return SILParameterInfo(newType.getASTType(), param.getConvention(), param.getOptions()); } } SmallVector LargeSILTypeMapper::getNewParameters(GenericEnvironment *env, CanSILFunctionType fnType, irgen::IRGenModule &IGM) { SmallVector newParams; for (SILParameterInfo param : fnType->getParameters()) { auto newParam = getNewParameter(env, param, IGM); newParams.push_back(newParam); } return newParams; } SmallVector LargeSILTypeMapper::getNewYields(GenericEnvironment *env, CanSILFunctionType fnType, irgen::IRGenModule &IGM) { SmallVector newYields; for (auto oldYield : fnType->getYields()) { auto newYieldAsParam = getNewParameter(env, oldYield, IGM); newYields.push_back(SILYieldInfo(newYieldAsParam.getInterfaceType(), newYieldAsParam.getConvention())); } return newYields; } SILType LargeSILTypeMapper::getNewTupleType(GenericEnvironment *GenericEnv, irgen::IRGenModule &Mod, const SILType &nonOptionalType, const SILType &storageType) { auto origType = nonOptionalType.getAs(); assert(origType && "Expected a tuple type"); SmallVector newElems; for (TupleTypeElt canElem : origType->getElements()) { auto origCanType = CanType(canElem.getType()); auto elem = SILType::getPrimitiveObjectType(origCanType); auto newElem = getNewSILType(GenericEnv, elem, Mod); newElems.emplace_back(newElem.getASTType(), canElem.getName()); } auto type = TupleType::get(newElems, nonOptionalType.getASTContext()); auto canType = CanType(type); SILType newSILType = SILType::getPrimitiveObjectType(canType); if (nonOptionalType.isAddress()) { newSILType = newSILType.getAddressType(); } if (nonOptionalType != storageType) { newSILType = SILType::getOptionalType(newSILType); } if (storageType.isAddress()) { newSILType = newSILType.getAddressType(); } return newSILType; } SILType LargeSILTypeMapper::getNewSILType(GenericEnvironment *GenericEnv, SILType storageType, irgen::IRGenModule &Mod) { // See if the type is already in the cache: auto typePair = std::make_pair(GenericEnv, storageType); if (oldToNewTypeMap.find(typePair) != oldToNewTypeMap.end()) { return oldToNewTypeMap[typePair]; } SILType nonOptionalType = storageType; if (auto optType = storageType.getOptionalObjectType()) { nonOptionalType = optType; } if (nonOptionalType.getAs()) { SILType newSILType = getNewTupleType(GenericEnv, Mod, nonOptionalType, storageType); auto typeToRet = isLargeLoadableType(GenericEnv, newSILType, Mod) ? newSILType.getAddressType() : newSILType; oldToNewTypeMap[typePair] = typeToRet; return typeToRet; } SILType newSILType = getNewOptionalFunctionType(GenericEnv, storageType, Mod); if (newSILType != storageType) { oldToNewTypeMap[typePair] = newSILType; return newSILType; } if (auto fnType = storageType.getAs()) { if (shouldTransformFunctionType(GenericEnv, fnType, Mod)) { auto newFnType = getNewSILFunctionType(GenericEnv, fnType, Mod); newSILType = SILType::getPrimitiveType(newFnType, storageType.getCategory()); } } else if (isLargeLoadableType(GenericEnv, storageType, Mod)) { newSILType = storageType.getAddressType(); } oldToNewTypeMap[typePair] = newSILType; return newSILType; } //===----------------------------------------------------------------------===// // StructLoweringState: shared state for the pass's analysis and transforms. //===----------------------------------------------------------------------===// namespace { struct StructLoweringState { SILFunction *F; irgen::IRGenModule &Mod; LargeSILTypeMapper &Mapper; // All large loadable function arguments that we modified SmallVector largeLoadableArgs; // All modified function signature function arguments SmallVector funcSigArgs; // All args for which we did a load llvm::MapVector argsToLoadedValueMap; // All applies for which we did an alloc llvm::MapVector applyRetToAllocMap; // reverse map of the one above llvm::MapVector allocToApplyRetMap; // All call sites with SILArgument that needs to be re-written // Calls are removed from the set when rewritten. SmallVector applies; // All MethodInst that use the large struct SmallVector methodInstsToMod; // Large loadable store instrs should call the outlined copy SmallVector storeInstsToMod; // All switch_enum instrs that should be converted to switch_enum_addr SmallVector switchEnumInstsToMod; // All struct_extract instrs that should be converted to struct_element_addr SmallVector structExtractInstsToMod; // All tuple instructions for which the return type is a function type SmallVector tupleInstsToMod; // All allock stack instructions to modify SmallVector allocStackInstsToMod; // All pointer to address instructions to modify SmallVector pointerToAddrkInstsToMod; // All Retain and release instrs should be replaced with _addr version SmallVector retainInstsToMod; SmallVector releaseInstsToMod; // All result types instrs for which we need to convert the ResultTy llvm::SetVector resultTyInstsToMod; // All instructions that use the large struct that are not covered above SmallVector instsToMod; // All function-exiting terminators (return or throw instructions). SmallVector returnInsts; // All (large type) return instructions that are modified SmallVector modReturnInsts; // All (large type) yield instructions that are modified SmallVector modYieldInsts; // All destroy_value instrs should be replaced with _addr version SmallVector destroyValueInstsToMod; // All debug instructions. // to be modified *only if* the operands are used in "real" instructions SmallVector debugInstsToMod; StructLoweringState(SILFunction *F, irgen::IRGenModule &Mod, LargeSILTypeMapper &Mapper) : F(F), Mod(Mod), Mapper(Mapper) {} bool isLargeLoadableType(CanSILFunctionType fnTy, SILType type) { return ::isLargeLoadableType(getSubstGenericEnvironment(fnTy), type, Mod); } SILType getNewSILType(CanSILFunctionType fnTy, SILType type) { return Mapper.getNewSILType(getSubstGenericEnvironment(fnTy), type, Mod); } bool containsDifferentFunctionSignature(CanSILFunctionType fnTy, SILType type) { return Mapper.containsDifferentFunctionSignature( getSubstGenericEnvironment(fnTy), Mod, type, getNewSILType(fnTy, type)); } bool hasLargeLoadableYields() { auto fnType = F->getLoweredFunctionType(); if (!fnType->isCoroutine()) return false; auto env = getSubstGenericEnvironment(fnType); for (auto yield : fnType->getYields()) { if (Mapper.shouldTransformParameter(env, yield, Mod)) return true; } return false; } }; } // end anonymous namespace //===----------------------------------------------------------------------===// // LargeValueVisitor: Map large loadable values to ValueStorage. //===----------------------------------------------------------------------===// namespace { class LargeValueVisitor { StructLoweringState &pass; PostOrderFunctionInfo postorderInfo; public: explicit LargeValueVisitor(StructLoweringState &pass) : pass(pass), postorderInfo(pass.F) {} void mapReturnInstrs(); void mapValueStorage(); protected: void visitApply(ApplySite applySite); void visitMethodInst(MethodInst *instr); void visitStoreInst(StoreInst *instr); void visitSwitchEnumInst(SwitchEnumInst *instr); void visitStructExtractInst(StructExtractInst *instr); void visitRetainInst(RetainValueInst *instr); void visitReleaseInst(ReleaseValueInst *instr); void visitResultTyInst(SingleValueInstruction *instr); void visitDebugValueInst(DebugValueInst *instr); void visitDestroyValueInst(DestroyValueInst *instr); void visitTupleInst(SingleValueInstruction *instr); void visitAllocStackInst(AllocStackInst *instr); void visitPointerToAddressInst(PointerToAddressInst *instr); void visitReturnInst(ReturnInst *instr); void visitYieldInst(YieldInst *instr); void visitDeallocInst(DeallocStackInst *instr); void visitInstr(SILInstruction *instr); }; } // end anonymous namespace void LargeValueVisitor::mapReturnInstrs() { for (auto *BB : postorderInfo.getReversePostOrder()) { if (BB->getTerminator()->isFunctionExiting()) pass.returnInsts.push_back(BB->getTerminator()); } } void LargeValueVisitor::mapValueStorage() { for (auto *BB : postorderInfo.getReversePostOrder()) { for (auto &II : *BB) { SILInstruction *currIns = &II; switch (currIns->getKind()) { case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::PartialApplyInst: { visitApply(ApplySite(currIns)); break; } case SILInstructionKind::ClassMethodInst: case SILInstructionKind::SuperMethodInst: case SILInstructionKind::ObjCMethodInst: case SILInstructionKind::ObjCSuperMethodInst: case SILInstructionKind::WitnessMethodInst: { // TODO Any more instructions to add here? auto *MI = cast(currIns); visitMethodInst(MI); break; } case SILInstructionKind::StructExtractInst: case SILInstructionKind::StructElementAddrInst: case SILInstructionKind::RefTailAddrInst: case SILInstructionKind::RefElementAddrInst: case SILInstructionKind::BeginAccessInst: case SILInstructionKind::InitEnumDataAddrInst: case SILInstructionKind::EnumInst: { // TODO Any more instructions to add here? visitResultTyInst(cast(currIns)); break; } case SILInstructionKind::StoreInst: { auto *SI = cast(currIns); visitStoreInst(SI); break; } case SILInstructionKind::RetainValueInst: { auto *RETI = cast(currIns); visitRetainInst(RETI); break; } case SILInstructionKind::ReleaseValueInst: { auto *RELI = cast(currIns); visitReleaseInst(RELI); break; } case SILInstructionKind::DebugValueInst: { auto *DI = cast(currIns); visitDebugValueInst(DI); break; } case SILInstructionKind::DestroyValueInst: { auto *DI = cast(currIns); visitDestroyValueInst(DI); break; } case SILInstructionKind::SwitchEnumInst: { auto *SEI = cast(currIns); visitSwitchEnumInst(SEI); break; } case SILInstructionKind::TupleElementAddrInst: case SILInstructionKind::TupleExtractInst: { visitTupleInst(cast(currIns)); break; } case SILInstructionKind::AllocStackInst: { auto *ASI = cast(currIns); visitAllocStackInst(ASI); break; } case SILInstructionKind::PointerToAddressInst: { auto *PTA = cast(currIns); visitPointerToAddressInst(PTA); break; } case SILInstructionKind::ReturnInst: { auto *RI = cast(currIns); visitReturnInst(RI); break; } case SILInstructionKind::YieldInst: { auto *YI = cast(currIns); visitYieldInst(YI); break; } case SILInstructionKind::DeallocStackInst: { auto *DI = cast(currIns); visitDeallocInst(DI); break; } default: { assert(!ApplySite::isa(currIns) && "Did not expect an ApplySite"); assert(!isa(currIns) && "Unhandled Method Inst"); visitInstr(currIns); break; } } } } } static bool modifiableApply(ApplySite applySite, irgen::IRGenModule &Mod) { // If the callee is a method then use the old ABI if (applySite.getSubstCalleeType()->getLanguage() == SILFunctionLanguage::C) { return false; } SILValue callee = applySite.getCallee(); if (auto site = ApplySite::isa(callee)) { return modifiableApply(site, Mod); } return true; } void LargeValueVisitor::visitApply(ApplySite applySite) { if (!modifiableApply(applySite, pass.Mod)) { return visitInstr(applySite.getInstruction()); } for (Operand &operand : applySite.getArgumentOperands()) { SILValue currOperand = operand.get(); SILType silType = currOperand->getType(); SILType newSilType = pass.getNewSILType(applySite.getSubstCalleeType(), silType); if (silType != newSilType || std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), currOperand) != pass.largeLoadableArgs.end() || std::find(pass.funcSigArgs.begin(), pass.funcSigArgs.end(), currOperand) != pass.funcSigArgs.end()) { pass.applies.push_back(applySite.getInstruction()); return; } } // For coroutines, we need to consider the yields, not the direct result // (which should always be void). if (auto beginApply = dyn_cast(applySite)) { for (auto yield : beginApply->getYieldedValues()) { auto oldYieldType = yield->getType(); auto newYieldType = pass.getNewSILType(beginApply->getSubstCalleeType(), oldYieldType); if (oldYieldType != newYieldType) { pass.applies.push_back(applySite.getInstruction()); return; } } return; } SILType currType = applySite.getType(); SILType newType = pass.getNewSILType(pass.F->getLoweredFunctionType(), currType); // We only care about function type results if (!pass.isLargeLoadableType(pass.F->getLoweredFunctionType(), currType) && (currType != newType)) { pass.applies.push_back(applySite.getInstruction()); return; } // Check callee - need new generic env: CanSILFunctionType origSILFunctionType = applySite.getSubstCalleeType(); GenericEnvironment *genEnvCallee = nullptr; auto newSILFunctionType = pass.Mapper.getNewSILFunctionType( genEnvCallee, origSILFunctionType, pass.Mod); if (origSILFunctionType != newSILFunctionType) { pass.applies.push_back(applySite.getInstruction()); } } static bool isMethodInstUnmodifiable(MethodInst *instr) { for (auto *user : instr->getUses()) { if (ApplySite::isa(user->getUser())) { ApplySite applySite = ApplySite(user->getUser()); if (applySite.getSubstCalleeType()->getLanguage() == SILFunctionLanguage::C) { return true; } } } return false; } void LargeValueVisitor::visitMethodInst(MethodInst *instr) { if (isMethodInstUnmodifiable(instr)) { // Do not change the method! visitInstr(instr); return; } SILType currSILType = instr->getType(); auto fnType = currSILType.castTo(); GenericEnvironment *genEnv = nullptr; if (fnType->isPolymorphic()) { genEnv = getSubstGenericEnvironment(fnType); } if (pass.Mapper.shouldTransformFunctionType(genEnv, fnType, pass.Mod)) { pass.methodInstsToMod.push_back(instr); return; } if (pass.Mapper.newResultsDiffer(genEnv, fnType->getResults(), pass.Mod)) { pass.methodInstsToMod.push_back(instr); } } void LargeValueVisitor::visitStoreInst(StoreInst *instr) { SILValue src = instr->getSrc(); if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), src) != pass.largeLoadableArgs.end()) { pass.storeInstsToMod.push_back(instr); } } bool LargeSILTypeMapper::shouldConvertBBArg(SILArgument *arg, irgen::IRGenModule &Mod) { auto *F = arg->getFunction(); SILType storageType = arg->getType(); GenericEnvironment *genEnv = getSubstGenericEnvironment(F); auto currCanType = storageType.getASTType(); if (auto funcType = dyn_cast(currCanType)) { if (funcType->isPolymorphic()) { genEnv = getSubstGenericEnvironment(funcType); } } SILType newSILType = getNewSILType(genEnv, storageType, Mod); // We (currently) only care about function signatures if (containsDifferentFunctionSignature(genEnv, Mod, storageType, newSILType)) { return true; } return false; } void LargeValueVisitor::visitSwitchEnumInst(SwitchEnumInst *instr) { SILValue operand = instr->getOperand(); if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), operand) != pass.largeLoadableArgs.end()) { pass.switchEnumInstsToMod.push_back(instr); return; } // In case we converted the target BB type of this enum, // to an address based one - need to modify unsigned numOfCases = instr->getNumCases(); SmallVector, 16> caseBBs; for (unsigned i = 0; i < numOfCases; ++i) { auto currCase = instr->getCase(i); auto *currBB = currCase.second; for (SILArgument *arg : currBB->getArguments()) { if (pass.Mapper.shouldConvertBBArg(arg, pass.Mod)) { SILType storageType = arg->getType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), storageType); if (newSILType.isAddress()) { pass.switchEnumInstsToMod.push_back(instr); return; } } } } } void LargeValueVisitor::visitStructExtractInst(StructExtractInst *instr) { SILValue operand = instr->getOperand(); if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), operand) != pass.largeLoadableArgs.end()) { pass.structExtractInstsToMod.push_back(instr); } } void LargeValueVisitor::visitRetainInst(RetainValueInst *instr) { for (Operand &operand : instr->getAllOperands()) { if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), operand.get()) != pass.largeLoadableArgs.end()) { pass.retainInstsToMod.push_back(instr); return; } } } void LargeValueVisitor::visitReleaseInst(ReleaseValueInst *instr) { for (Operand &operand : instr->getAllOperands()) { if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), operand.get()) != pass.largeLoadableArgs.end()) { pass.releaseInstsToMod.push_back(instr); return; } } } void LargeValueVisitor::visitDebugValueInst(DebugValueInst *instr) { for (Operand &operand : instr->getAllOperands()) { if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), operand.get()) != pass.largeLoadableArgs.end()) { pass.debugInstsToMod.push_back(instr); } } } void LargeValueVisitor::visitDestroyValueInst(DestroyValueInst *instr) { for (Operand &operand : instr->getAllOperands()) { if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), operand.get()) != pass.largeLoadableArgs.end()) { pass.destroyValueInstsToMod.push_back(instr); } } } void LargeValueVisitor::visitResultTyInst(SingleValueInstruction *instr) { SILType currSILType = instr->getType().getObjectType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), currSILType); if (currSILType != newSILType) { pass.resultTyInstsToMod.insert(instr); } auto *SEI = dyn_cast(instr); if (SEI) { visitStructExtractInst(SEI); } else { visitInstr(instr); } } void LargeValueVisitor::visitTupleInst(SingleValueInstruction *instr) { SILType currSILType = instr->getType().getObjectType(); if (auto funcType = getInnerFunctionType(currSILType)) { GenericEnvironment *genEnv = getSubstGenericEnvironment(instr->getFunction()); if (!genEnv && funcType->isPolymorphic()) { genEnv = getSubstGenericEnvironment(funcType); } auto newSILFunctionType = pass.Mapper.getNewSILFunctionType(genEnv, funcType, pass.Mod); if (funcType != newSILFunctionType) { pass.tupleInstsToMod.push_back(instr); } } visitInstr(instr); } void LargeValueVisitor::visitAllocStackInst(AllocStackInst *instr) { SILType currSILType = instr->getType().getObjectType(); if (pass.containsDifferentFunctionSignature(pass.F->getLoweredFunctionType(), currSILType)) { pass.allocStackInstsToMod.push_back(instr); } } void LargeValueVisitor::visitPointerToAddressInst(PointerToAddressInst *instr) { SILType currSILType = instr->getType().getObjectType(); if (pass.containsDifferentFunctionSignature(pass.F->getLoweredFunctionType(), currSILType)) { pass.pointerToAddrkInstsToMod.push_back(instr); } } static bool modNonFuncTypeResultType(SILFunction *F, irgen::IRGenModule &Mod) { GenericEnvironment *genEnv = getSubstGenericEnvironment(F); auto loweredTy = F->getLoweredFunctionType(); return modNonFuncTypeResultType(genEnv, loweredTy, Mod); } void LargeValueVisitor::visitReturnInst(ReturnInst *instr) { if (!modResultType(pass.F, pass.Mod, pass.Mapper)) { visitInstr(instr); } else if (modNonFuncTypeResultType(pass.F, pass.Mod)) { pass.modReturnInsts.push_back(instr); } // else: function signature return instructions remain as-is } void LargeValueVisitor::visitYieldInst(YieldInst *instr) { if (!modYieldType(pass.F, pass.Mod, pass.Mapper)) { visitInstr(instr); } else { pass.modYieldInsts.push_back(instr); } // else: function signature return instructions remain as-is } void LargeValueVisitor::visitDeallocInst(DeallocStackInst *instr) { auto opInstr = instr->getOperand(); if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), opInstr) != pass.largeLoadableArgs.end()) { auto *opAsInstr = dyn_cast(opInstr); assert(opAsInstr && "Expected an alloc stack instruction"); assert(pass.allocToApplyRetMap.find(opAsInstr) != pass.allocToApplyRetMap.end() && "Unexpected dealloc instr!"); (void)opAsInstr; } } void LargeValueVisitor::visitInstr(SILInstruction *instr) { for (Operand &operand : instr->getAllOperands()) { if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), operand.get()) != pass.largeLoadableArgs.end()) { pass.instsToMod.push_back(instr); // will be replaced later by the load / alloc_stack: pass.argsToLoadedValueMap[operand.get()] = operand.get(); } } } //===----------------------------------------------------------------------===// // LoadableStorageAllocation: Generate alloc_stack and address projections // for all loadable types we pass around. //===----------------------------------------------------------------------===// namespace { class LoadableStorageAllocation { public: StructLoweringState &pass; explicit LoadableStorageAllocation(StructLoweringState &pass) : pass(pass) {} void allocateLoadableStorage(); void replaceLoad(LoadInst *unoptimizableLoad); protected: void replaceLoadWithCopyAddr(LoadInst *optimizableLoad); void replaceLoadWithCopyAddrForModifiable(LoadInst *unoptimizableLoad); void convertIndirectFunctionArgs(); void insertIndirectReturnArgs(); void convertIndirectFunctionPointerArgsForUnmodifiable(); void convertIndirectBasicBlockArgs(); void convertApplyResults(); void allocateForArg(SILValue value); AllocStackInst *allocateForApply(SILInstruction *apply, SILType type); SILArgument *replaceArgType(SILBuilder &argBuilder, SILArgument *arg, SILType newSILType); }; } // end anonymous namespace static AllocStackInst *allocate(StructLoweringState &pass, SILType type) { assert(type.isObject()); // Insert an alloc_stack at the beginning of the function. SILBuilderWithScope allocBuilder(&*pass.F->begin()); // Don't put any variable debug info into the alloc_stack, there will be a // debug_value inserted later. TODO: It may be more elegant to insert // the variable info into the alloc_stack instead of additionally generating a // debug_value. AllocStackInst *alloc = allocBuilder.createAllocStack( RegularLocation::getAutoGeneratedLocation(), type); // Insert dealloc_stack at the end(s) of the function. for (TermInst *termInst : pass.returnInsts) { SILBuilderWithScope deallocBuilder(termInst); deallocBuilder.createDeallocStack( RegularLocation::getAutoGeneratedLocation(), alloc); } return alloc; } static StoreOwnershipQualifier getStoreInitOwnership(StructLoweringState &pass, SILType type) { if (!pass.F->hasOwnership()) { return StoreOwnershipQualifier::Unqualified; } else if (type.isTrivial(*pass.F)) { return StoreOwnershipQualifier::Trivial; } else { return StoreOwnershipQualifier::Init; } } static StoreInst *createStoreInit(StructLoweringState &pass, SILBasicBlock::iterator where, SILLocation loc, SILValue value, SILValue address) { SILBuilderWithScope storeBuilder(where); return storeBuilder.createStore(loc, value, address, getStoreInitOwnership(pass, value->getType())); } static SILInstruction *createOutlinedCopyCall(SILBuilder ©Builder, SILValue src, SILValue tgt, StructLoweringState &pass, SILLocation *loc = nullptr) { SILLocation locToUse = loc ? *loc : copyBuilder.getInsertionPoint()->getLoc(); auto *copy = copyBuilder.createCopyAddr(locToUse, src, tgt, IsTake, IsInitialization); return copy; } void LoadableStorageAllocation::replaceLoadWithCopyAddr( LoadInst *optimizableLoad) { SILValue value = optimizableLoad->getOperand(); auto allocInstr = allocate(pass, value->getType().getObjectType()); SILBuilderWithScope outlinedBuilder(optimizableLoad); createOutlinedCopyCall(outlinedBuilder, value, allocInstr, pass); for (auto *user : optimizableLoad->getUses()) { SILInstruction *userIns = user->getUser(); switch (userIns->getKind()) { case SILInstructionKind::CopyAddrInst: case SILInstructionKind::DeallocStackInst: break; case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::PartialApplyInst: { if (std::find(pass.applies.begin(), pass.applies.end(), userIns) == pass.applies.end()) { pass.applies.push_back(userIns); } break; } case SILInstructionKind::YieldInst: // The rewrite is enough. break; case SILInstructionKind::RetainValueInst: { auto *insToInsert = cast(userIns); pass.retainInstsToMod.push_back(insToInsert); break; } case SILInstructionKind::ReleaseValueInst: { auto *insToInsert = cast(userIns); pass.releaseInstsToMod.push_back(insToInsert); break; } case SILInstructionKind::StoreInst: { auto *insToInsert = cast(userIns); pass.storeInstsToMod.push_back(insToInsert); break; } case SILInstructionKind::DebugValueInst: { auto *insToInsert = cast(userIns); pass.debugInstsToMod.push_back(insToInsert); break; } case SILInstructionKind::DestroyValueInst: { auto *insToInsert = cast(userIns); pass.destroyValueInstsToMod.push_back(insToInsert); break; } case SILInstructionKind::StructExtractInst: { auto *instToInsert = cast(userIns); if (std::find(pass.structExtractInstsToMod.begin(), pass.structExtractInstsToMod.end(), instToInsert) == pass.structExtractInstsToMod.end()) { pass.structExtractInstsToMod.push_back(instToInsert); } break; } case SILInstructionKind::SwitchEnumInst: { auto *instToInsert = cast(userIns); if (std::find(pass.switchEnumInstsToMod.begin(), pass.switchEnumInstsToMod.end(), instToInsert) == pass.switchEnumInstsToMod.end()) { pass.switchEnumInstsToMod.push_back(instToInsert); } break; } default: llvm_unreachable("Unexpected instruction"); } } optimizableLoad->replaceAllUsesWith(allocInstr); optimizableLoad->getParent()->erase(optimizableLoad); } static bool isYieldUseRewritable(StructLoweringState &pass, YieldInst *inst, Operand *operand) { assert(inst == operand->getUser()); return pass.isLargeLoadableType(pass.F->getLoweredFunctionType(), operand->get()->getType()); } /// Does the value's uses contain instructions that *must* be rewrites? static bool hasMandatoryRewriteUse(StructLoweringState &pass, SILValue value) { for (auto *user : value->getUses()) { SILInstruction *userIns = user->getUser(); switch (userIns->getKind()) { case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::PartialApplyInst: { ApplySite site(userIns); SILValue callee = site.getCallee(); if (callee == value) { break; } SILType currType = value->getType().getObjectType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), currType); if (currType == newSILType) { break; } return true; } case SILInstructionKind::YieldInst: if (isYieldUseRewritable(pass, cast(userIns), user)) return true; break; default: break; } } return false; } void LoadableStorageAllocation::replaceLoadWithCopyAddrForModifiable( LoadInst *unoptimizableLoad) { if (!hasMandatoryRewriteUse(pass, unoptimizableLoad)) { return; } SILValue value = unoptimizableLoad->getOperand(); AllocStackInst *alloc = allocate(pass, value->getType().getObjectType()); SILBuilderWithScope outlinedBuilder(unoptimizableLoad); createOutlinedCopyCall(outlinedBuilder, value, alloc, pass); SmallVector usesToMod; for (auto *use : unoptimizableLoad->getUses()) { SILInstruction *userIns = use->getUser(); switch (userIns->getKind()) { case SILInstructionKind::CopyAddrInst: case SILInstructionKind::DeallocStackInst: break; case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::PartialApplyInst: { ApplySite site(userIns); if (!modifiableApply(site, pass.Mod)) { break; } SILValue callee = site.getCallee(); if (callee == unoptimizableLoad) { break; } SILType currType = unoptimizableLoad->getType().getObjectType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), currType); if (currType == newSILType) { break; } if (std::find(pass.applies.begin(), pass.applies.end(), userIns) == pass.applies.end()) { pass.applies.push_back(userIns); } usesToMod.push_back(use); break; } case SILInstructionKind::YieldInst: { if (isYieldUseRewritable(pass, cast(userIns), use)) usesToMod.push_back(use); break; } case SILInstructionKind::RetainValueInst: { auto *insToInsert = cast(userIns); pass.retainInstsToMod.push_back(insToInsert); usesToMod.push_back(use); break; } case SILInstructionKind::ReleaseValueInst: { auto *insToInsert = cast(userIns); pass.releaseInstsToMod.push_back(insToInsert); usesToMod.push_back(use); break; } case SILInstructionKind::StoreInst: { auto *insToInsert = cast(userIns); pass.storeInstsToMod.push_back(insToInsert); usesToMod.push_back(use); break; } case SILInstructionKind::DebugValueInst: { auto *insToInsert = cast(userIns); pass.debugInstsToMod.push_back(insToInsert); usesToMod.push_back(use); break; } case SILInstructionKind::DestroyValueInst: { auto *insToInsert = cast(userIns); pass.destroyValueInstsToMod.push_back(insToInsert); usesToMod.push_back(use); break; } case SILInstructionKind::StructExtractInst: { auto *instToInsert = cast(userIns); pass.structExtractInstsToMod.push_back(instToInsert); usesToMod.push_back(use); break; } case SILInstructionKind::SwitchEnumInst: { auto *instToInsert = cast(userIns); pass.switchEnumInstsToMod.push_back(instToInsert); usesToMod.push_back(use); break; } default: break; } } while (!usesToMod.empty()) { auto *use = usesToMod.pop_back_val(); use->set(alloc); } } void LoadableStorageAllocation::allocateLoadableStorage() { // We need to map all functions exits // required for Apply result's allocations // Else we might get the following error: // "stack dealloc does not match most recent stack alloc" // When we dealloc later LargeValueVisitor(pass).mapReturnInstrs(); if (modifiableFunction(pass.F->getLoweredFunctionType())) { // Turn by-value function args to by-address ones convertIndirectFunctionArgs(); } else { convertIndirectFunctionPointerArgsForUnmodifiable(); } convertApplyResults(); // Populate the pass' data structs LargeValueVisitor(pass).mapValueStorage(); // Turn by-value BB args to by-address ones convertIndirectBasicBlockArgs(); // Create an AllocStack for every used large loadable type in the function. for (auto &argToAlloc : pass.argsToLoadedValueMap) { assert(argToAlloc.first == argToAlloc.second); allocateForArg(argToAlloc.first); } } SILArgument *LoadableStorageAllocation::replaceArgType(SILBuilder &argBuilder, SILArgument *arg, SILType newSILType) { SILValue undef = SILUndef::get(pass.F, newSILType); SmallVector useList(arg->use_begin(), arg->use_end()); for (auto *use : useList) { use->set(undef); } // Make sure that this is an argument we want to replace. assert(std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), arg) == pass.largeLoadableArgs.end()); arg = arg->getParent()->replaceFunctionArgument( arg->getIndex(), newSILType, OwnershipKind::None, arg->getDecl()); for (auto *use : useList) { use->set(arg); } return arg; } void LoadableStorageAllocation::insertIndirectReturnArgs() { GenericEnvironment *genEnv = getSubstGenericEnvironment(pass.F); auto loweredTy = pass.F->getLoweredFunctionType(); SILType resultStorageType = loweredTy->getAllResultsSubstType(pass.F->getModule(), pass.F->getTypeExpansionContext()); auto canType = resultStorageType.getASTType(); if (canType->hasTypeParameter()) { assert(genEnv && "Expected a GenericEnv"); canType = genEnv->mapTypeIntoContext(canType)->getCanonicalType(); } resultStorageType = SILType::getPrimitiveObjectType(canType); auto newResultStorageType = pass.F->getLoweredType(pass.getNewSILType(loweredTy, resultStorageType)); auto &ctx = pass.F->getModule().getASTContext(); auto var = new (ctx) ParamDecl( SourceLoc(), SourceLoc(), ctx.getIdentifier("$return_value"), SourceLoc(), ctx.getIdentifier("$return_value"), pass.F->getDeclContext()); var->setSpecifier(ParamSpecifier::InOut); pass.F->begin()->insertFunctionArgument( 0, newResultStorageType.getAddressType(), OwnershipKind::None, var); } void LoadableStorageAllocation::convertIndirectFunctionArgs() { SILBasicBlock *entry = pass.F->getEntryBlock(); SILBuilderWithScope argBuilder(entry->begin()); for (SILArgument *arg : entry->getArguments()) { SILType storageType = arg->getType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), storageType); if (newSILType != storageType) { ValueOwnershipKind ownership = arg->getOwnershipKind(); arg = replaceArgType(argBuilder, arg, newSILType); if (pass.isLargeLoadableType(pass.F->getLoweredFunctionType(), storageType)) { // Add to largeLoadableArgs if and only if it wasn't a modified function // signature arg pass.largeLoadableArgs.push_back(arg); } else { arg->setOwnershipKind(ownership); pass.funcSigArgs.push_back(arg); } } } // Convert the result type to indirect if necessary: if (modNonFuncTypeResultType(pass.F, pass.Mod)) { insertIndirectReturnArgs(); } } static void convertBBArgType(SILBuilder &argBuilder, SILType newSILType, SILArgument *arg) { SILValue undef = SILUndef::get(argBuilder.getFunction(), newSILType); SmallVector useList(arg->use_begin(), arg->use_end()); for (auto *use : useList) { use->set(undef); } arg = arg->getParent()->replacePhiArgument(arg->getIndex(), newSILType, arg->getOwnershipKind()); for (auto *use : useList) { use->set(arg); } } static bool containsFunctionType(CanType ty) { if (auto tuple = dyn_cast(ty)) { for (auto elt : tuple.getElementTypes()) { if (containsFunctionType(elt)) return true; } return false; } if (auto optionalType = ty.getOptionalObjectType()) { return containsFunctionType(optionalType); } return isa(ty); } void LoadableStorageAllocation::convertApplyResults() { for (auto &BB : *pass.F) { for (auto &II : BB) { auto *currIns = &II; auto applySite = FullApplySite::isa(currIns); if (!applySite) { continue; } if (!modifiableApply(applySite, pass.Mod)) { continue; } CanSILFunctionType origSILFunctionType = applySite.getSubstCalleeType(); GenericEnvironment *genEnv = getSubstGenericEnvironment(origSILFunctionType); if (!pass.Mapper.shouldTransformResults(genEnv, origSILFunctionType, pass.Mod)) { continue; } auto resultStorageType = origSILFunctionType->getAllResultsInterfaceType(); if (!pass.isLargeLoadableType(origSILFunctionType, resultStorageType)) { // Make sure it contains a function type auto numFuncTy = llvm::count_if(origSILFunctionType->getResults(), [](const SILResultInfo &origResult) { auto resultStorageTy = origResult.getSILStorageInterfaceType(); return containsFunctionType(resultStorageTy.getASTType()); }); assert(numFuncTy != 0 && "Expected a SILFunctionType inside the result Type"); (void)numFuncTy; continue; } auto resultContextTy = origSILFunctionType->substInterfaceType( pass.F->getModule(), resultStorageType, pass.F->getTypeExpansionContext()); auto newSILType = pass.getNewSILType(origSILFunctionType, resultContextTy); auto *newVal = allocateForApply(currIns, newSILType.getObjectType()); if (auto apply = dyn_cast(currIns)) { apply->replaceAllUsesWith(newVal); } else { auto tryApplyIns = cast(currIns); auto *normalBB = tryApplyIns->getNormalBB(); SILBuilderWithScope argBuilder(normalBB->begin()); assert(normalBB->getNumArguments() == 1 && "Expected only one arg for try_apply normal BB"); auto arg = normalBB->getArgument(0); arg->replaceAllUsesWith(newVal); auto emptyTy = SILType::getPrimitiveObjectType( TupleType::getEmpty(argBuilder.getModule().getASTContext())); convertBBArgType(argBuilder, emptyTy, arg); } } } } void LoadableStorageAllocation:: convertIndirectFunctionPointerArgsForUnmodifiable() { SILBasicBlock *entry = pass.F->getEntryBlock(); SILBuilderWithScope argBuilder(entry->begin()); for (SILArgument *arg : entry->getArguments()) { SILType storageType = arg->getType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), storageType); if (pass.containsDifferentFunctionSignature(pass.F->getLoweredFunctionType(), storageType)) { auto *castInstr = argBuilder.createUncheckedReinterpretCast( RegularLocation(const_cast(arg->getDecl())), arg, newSILType); arg->replaceAllUsesWith(castInstr); castInstr->setOperand(0, arg); } } } void LoadableStorageAllocation::convertIndirectBasicBlockArgs() { SILBasicBlock *entry = pass.F->getEntryBlock(); for (SILBasicBlock &BB : *pass.F) { if (&BB == entry) { // Already took care of function args continue; } SILBuilderWithScope argBuilder(BB.begin()); for (SILArgument *arg : BB.getArguments()) { if (!pass.Mapper.shouldConvertBBArg(arg, pass.Mod)) { continue; } SILType storageType = arg->getType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), storageType); // We don't change the type from object to address for function args: // a tuple with both a large type and a function arg should remain // as an object type for now if (storageType.isObject()) { newSILType = newSILType.getObjectType(); } convertBBArgType(argBuilder, newSILType, arg); } } } void LoadableStorageAllocation::allocateForArg(SILValue value) { if (auto *allocInstr = dyn_cast(value)) { // Special case: the value was already an Alloc // This happens in case of values from apply results (for example) // we *should* add a load for the current uses. // Said load should happen before the first use // As such add it right after the apply() LoadInst *load = nullptr; assert(pass.allocToApplyRetMap.find(allocInstr) != pass.allocToApplyRetMap.end() && "Alloc is not for apply results"); auto *applyInst = pass.allocToApplyRetMap[allocInstr]; assert(applyInst && "Value is not an apply"); auto II = applyInst->getIterator(); SILBuilderWithScope loadBuilder(II); if (auto *tryApply = dyn_cast(applyInst)) { auto *tgtBB = tryApply->getNormalBB(); assert(tgtBB && "Could not find try apply's target BB"); loadBuilder.setInsertionPoint(tgtBB->begin()); } else { ++II; loadBuilder.setInsertionPoint(II); } if (!pass.F->hasOwnership()) { load = loadBuilder.createLoad(applyInst->getLoc(), value, LoadOwnershipQualifier::Unqualified); } else { load = loadBuilder.createLoad(applyInst->getLoc(), value, LoadOwnershipQualifier::Take); } pass.argsToLoadedValueMap[value] = load; return; } assert(!ApplySite::isa(value) && "Unexpected instruction"); // Find the first non-AllocStackInst and use its scope when creating // the new SILBuilder. An AllocStackInst does not directly cause any // code to be generated. The location of an AllocStackInst carries information // about the source variable; it doesn't matter where in the instruction // stream the AllocStackInst is located. auto BBIter = pass.F->begin()->begin(); SILInstruction *FirstNonAllocStack = &*BBIter; while (isa(FirstNonAllocStack) && BBIter != pass.F->begin()->end()) { ++BBIter; FirstNonAllocStack = &*BBIter; } SILBuilderWithScope allocBuilder(&*pass.F->begin()->begin(), FirstNonAllocStack); AllocStackInst *allocInstr = allocBuilder.createAllocStack( RegularLocation::getAutoGeneratedLocation(), value->getType()); LoadInst *loadCopy = nullptr; auto *applyOutlinedCopy = createOutlinedCopyCall(allocBuilder, value, allocInstr, pass); if (!pass.F->hasOwnership()) { loadCopy = allocBuilder.createLoad(applyOutlinedCopy->getLoc(), allocInstr, LoadOwnershipQualifier::Unqualified); } else { loadCopy = allocBuilder.createLoad(applyOutlinedCopy->getLoc(), allocInstr, LoadOwnershipQualifier::Take); } pass.argsToLoadedValueMap[value] = loadCopy; // Insert stack deallocations. for (TermInst *termInst : pass.returnInsts) { SILBuilderWithScope deallocBuilder(termInst); deallocBuilder.createDeallocStack(allocInstr->getLoc(), allocInstr); } } AllocStackInst * LoadableStorageAllocation::allocateForApply(SILInstruction *apply, SILType type) { SILBuilderWithScope allocBuilder(&*pass.F->begin()); SILLocation Loc = apply->getLoc(); if (dyn_cast_or_null(Loc.getAsASTNode())) // FIXME: Remove this. This is likely indicative of a bug earlier in the // pipeline. An apply instruction should not have a VarDecl as location. Loc = RegularLocation::getAutoGeneratedLocation(); auto *allocInstr = allocBuilder.createAllocStack(Loc, type); pass.largeLoadableArgs.push_back(allocInstr); pass.allocToApplyRetMap[allocInstr] = apply; pass.applyRetToAllocMap[apply] = allocInstr; for (TermInst *termInst : pass.returnInsts) { SILBuilderWithScope deallocBuilder(termInst); deallocBuilder.createDeallocStack(allocInstr->getLoc(), allocInstr); } return allocInstr; } //===----------------------------------------------------------------------===// // LoadableByAddress: Top-Level Function Transform. //===----------------------------------------------------------------------===// namespace { class LoadableByAddress : public SILModuleTransform { /// The entry point to this function transformation. void run() override; void runOnFunction(SILFunction *F); private: void updateLoweredTypes(SILFunction *F); void recreateSingleApply(SILInstruction *applyInst, SmallVectorImpl &Delete); bool recreateApply(SILInstruction &I, SmallVectorImpl &Delete); bool recreateTupleInstr(SILInstruction &I, SmallVectorImpl &Delete); bool recreateConvInstr(SILInstruction &I, SmallVectorImpl &Delete); bool recreateBuiltinInstr(SILInstruction &I, SmallVectorImpl &Delete); bool recreateLoadInstr(SILInstruction &I, SmallVectorImpl &Delete); bool recreateUncheckedEnumDataInstr(SILInstruction &I, SmallVectorImpl &Delete); bool recreateUncheckedTakeEnumDataAddrInst(SILInstruction &I, SmallVectorImpl &Delete); bool fixStoreToBlockStorageInstr(SILInstruction &I, SmallVectorImpl &Delete); bool recreateDifferentiabilityWitnessFunction( SILInstruction &I, SmallVectorImpl &Delete); bool shouldTransformGlobal(SILGlobalVariable *global); bool shouldTransformInitExprOfGlobal(SILGlobalVariable *global); private: llvm::SetVector modFuncs; llvm::SetVector conversionInstrs; llvm::SetVector builtinInstrs; llvm::SetVector loadInstrsOfFunc; llvm::SetVector uncheckedEnumDataOfFunc; llvm::SetVector uncheckedTakeEnumDataAddrOfFunc; llvm::SetVector storeToBlockStorageInstrs; llvm::SetVector modApplies; llvm::MapVector allApplyRetToAllocMap; public: LargeSILTypeMapper MapperCache; }; } // end anonymous namespace /// Given that we've allocated space to hold a scalar value, try to rewrite /// the uses of the scalar to be uses of the address. static void rewriteUsesOfSscalar(StructLoweringState &pass, SILValue address, SILValue scalar, StoreInst *storeToAddress) { // Copy the uses, since we're going to edit them. SmallVector uses(scalar->getUses()); for (Operand *scalarUse : uses) { SILInstruction *user = scalarUse->getUser(); if (ApplySite::isa(user)) { ApplySite site(user); if (modifiableApply(site, pass.Mod)) { // Just rewrite the operand in-place. This will produce a temporary // type error, but we should fix that up when we rewrite the apply's // function type. scalarUse->set(address); } } else if (isa(user)) { // The rules for the yield are changing anyway, so we can just rewrite // it in-place. scalarUse->set(address); } else if (auto *storeUser = dyn_cast(user)) { // Don't rewrite the store to the allocation. if (storeUser == storeToAddress) continue; // Optimization: replace with copy_addr to reduce code size assert(std::find(pass.storeInstsToMod.begin(), pass.storeInstsToMod.end(), storeUser) == pass.storeInstsToMod.end() && "Did not expect this instr in storeInstsToMod"); SILBuilderWithScope copyBuilder(storeUser); SILValue dest = storeUser->getDest(); createOutlinedCopyCall(copyBuilder, address, dest, pass); storeUser->eraseFromParent(); } else if (auto *dbgInst = dyn_cast(user)) { SILBuilder dbgBuilder(dbgInst, dbgInst->getDebugScope()); // Rewrite the debug_value to point to the variable in the alloca. dbgBuilder.createDebugValueAddr(dbgInst->getLoc(), address, *dbgInst->getVarInfo()); dbgInst->eraseFromParent(); } } } static void allocateAndSetForInstResult(StructLoweringState &pass, SILValue instResult, SILInstruction *inst) { auto alloc = allocate(pass, instResult->getType()); auto II = inst->getIterator(); ++II; auto store = createStoreInit(pass, II, inst->getLoc(), instResult, alloc); // Traverse all the uses of instResult - see if we can replace rewriteUsesOfSscalar(pass, alloc, instResult, store); } static void allocateAndSetForArgument(StructLoweringState &pass, SILArgument *value, SILInstruction *user) { AllocStackInst *alloc = allocate(pass, value->getType()); SILLocation loc = user->getLoc(); loc.markAutoGenerated(); // Store the value into the allocation. auto II = value->getParent()->begin(); if (II == alloc->getParent()->begin()) { // Store should happen *after* the allocation. ++II; } auto store = createStoreInit(pass, II, loc, value, alloc); // Traverse all the uses of value - see if we can replace rewriteUsesOfSscalar(pass, alloc, value, store); } static bool allUsesAreReplaceable(StructLoweringState &pass, SingleValueInstruction *instr) { for (auto *user : instr->getUses()) { SILInstruction *userIns = user->getUser(); switch (userIns->getKind()) { case SILInstructionKind::RetainValueInst: case SILInstructionKind::ReleaseValueInst: case SILInstructionKind::StoreInst: case SILInstructionKind::DebugValueInst: case SILInstructionKind::DestroyValueInst: break; case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::PartialApplyInst: { // Replaceable only if it is not the function pointer ApplySite site(userIns); if (!modifiableApply(site, pass.Mod)) { return false; } SILValue callee = site.getCallee(); if (callee == instr) { return false; } SILType currType = instr->getType().getObjectType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), currType); if (currType == newSILType) { return false; } break; } case SILInstructionKind::YieldInst: if (!isYieldUseRewritable(pass, cast(userIns), user)) return false; break; case SILInstructionKind::StructExtractInst: case SILInstructionKind::SwitchEnumInst: break; default: return false; } } return true; } void LoadableStorageAllocation::replaceLoad(LoadInst *load) { if (allUsesAreReplaceable(pass, load)) { replaceLoadWithCopyAddr(load); } else { replaceLoadWithCopyAddrForModifiable(load); } } static void allocateAndSet(StructLoweringState &pass, LoadableStorageAllocation &allocator, SILValue operand, SILInstruction *user, Operand &opd) { if (isa(operand)) { auto alloc = allocate(pass, operand->getType()); opd.set(alloc); return; } auto inst = operand->getDefiningInstruction(); if (!inst) { allocateAndSetForArgument(pass, cast(operand), user); return; } if (auto *load = dyn_cast(operand)) { allocator.replaceLoad(load); } else { // TODO: peephole: special handling of known cases: // ApplyInst, TupleExtractInst allocateAndSetForInstResult(pass, operand, inst); } } /// Rewrite all of the large-loadable operands in the given list. static void allocateAndSetAll(StructLoweringState &pass, LoadableStorageAllocation &allocator, SILInstruction *user, MutableArrayRef operands) { for (Operand &operand : operands) { SILValue value = operand.get(); SILType silType = value->getType(); if (pass.isLargeLoadableType(pass.F->getLoweredFunctionType(), silType)) { allocateAndSet(pass, allocator, value, user, operand); } } } static void retypeTupleInstr(SingleValueInstruction *instr, IRGenModule &Mod, LargeSILTypeMapper &Mapper) { SILType currSILType = instr->getType(); auto funcType = getInnerFunctionType(currSILType); assert(funcType && "Expected a function Type"); GenericEnvironment *genEnv = getSubstGenericEnvironment(instr->getFunction()); if (!genEnv && funcType->getSubstGenericSignature()) { genEnv = getSubstGenericEnvironment(funcType); } SILType newSILType = Mapper.getNewSILType(genEnv, currSILType, Mod); if (currSILType == newSILType) { return; } auto II = instr->getIterator(); ++II; SILBuilderWithScope Builder(II); SingleValueInstruction *newInstr = nullptr; switch (instr->getKind()) { // Add cast to the new sil function type: case SILInstructionKind::TupleExtractInst: { auto extractInst = cast(instr); newInstr = Builder.createTupleExtract( extractInst->getLoc(), extractInst->getOperand(), extractInst->getFieldIndex(), newSILType.getObjectType()); break; } case SILInstructionKind::TupleElementAddrInst: { auto elementAddrInst = cast(instr); newInstr = Builder.createTupleElementAddr( elementAddrInst->getLoc(), elementAddrInst->getOperand(), elementAddrInst->getFieldIndex(), newSILType.getAddressType()); break; } default: llvm_unreachable("Unexpected instruction inside tupleInstsToMod"); } instr->replaceAllUsesWith(newInstr); instr->eraseFromParent(); } static SILValue createCopyOfEnum(StructLoweringState &pass, SwitchEnumInst *orig) { auto value = orig->getOperand(); auto type = value->getType(); if (type.isObject()) { // support for non-address operands / enums auto *alloc = allocate(pass, type); createStoreInit(pass, orig->getIterator(), orig->getLoc(), value, alloc); return alloc; } auto alloc = allocate(pass, type.getObjectType()); SILBuilderWithScope copyBuilder(orig); createOutlinedCopyCall(copyBuilder, value, alloc, pass); return alloc; } static void createResultTyInstrAndLoad(LoadableStorageAllocation &allocator, SingleValueInstruction *instr, StructLoweringState &pass) { bool updateResultTy = pass.resultTyInstsToMod.count(instr) != 0; if (updateResultTy) { pass.resultTyInstsToMod.remove(instr); } SILBuilderWithScope builder(instr); auto *currStructExtractInst = dyn_cast(instr); assert(currStructExtractInst && "Expected StructExtractInst"); SingleValueInstruction *newInstr = builder.createStructElementAddr( currStructExtractInst->getLoc(), currStructExtractInst->getOperand(), currStructExtractInst->getField(), currStructExtractInst->getType().getAddressType()); // Load the struct element then see if we can get rid of the load: LoadInst *loadArg = nullptr; if (!pass.F->hasOwnership()) { loadArg = builder.createLoad(newInstr->getLoc(), newInstr, LoadOwnershipQualifier::Unqualified); } else { loadArg = builder.createLoad(newInstr->getLoc(), newInstr, LoadOwnershipQualifier::Take); } instr->replaceAllUsesWith(loadArg); instr->getParent()->erase(instr); // If the load is of a function type - do not replace it. if (isFuncOrOptionalFuncType(loadArg->getType())) { return; } allocator.replaceLoad(loadArg); if (updateResultTy) { pass.resultTyInstsToMod.insert(newInstr); } } static void rewriteFunction(StructLoweringState &pass, LoadableStorageAllocation &allocator) { bool repeat = false; llvm::SetVector currentModApplies; do { while (!pass.switchEnumInstsToMod.empty()) { auto *instr = pass.switchEnumInstsToMod.pop_back_val(); /* unchecked_take_enum_data_addr can be destructive. * work on a copy instead of the original enum */ auto copiedValue = createCopyOfEnum(pass, instr); SILBuilderWithScope enumBuilder(instr); unsigned numOfCases = instr->getNumCases(); SmallVector, 16> caseBBs; for (unsigned i = 0; i < numOfCases; ++i) { auto currCase = instr->getCase(i); auto *currBB = currCase.second; SILBuilderWithScope argBuilder(currBB->begin()); assert(currBB->getNumArguments() <= 1 && "Unhandled BB Type"); EnumElementDecl *decl = currCase.first; for (SILArgument *arg : currBB->getArguments()) { SILType storageType = arg->getType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), storageType); if (storageType == newSILType) { newSILType = newSILType.getAddressType(); } auto *newArg = argBuilder.createUncheckedTakeEnumDataAddr( instr->getLoc(), copiedValue, decl, newSILType.getAddressType()); arg->replaceAllUsesWith(newArg); currBB->eraseArgument(0); // Load the enum addr then see if we can get rid of the load: LoadInst *loadArg = nullptr; if (!pass.F->hasOwnership()) { loadArg = argBuilder.createLoad( newArg->getLoc(), newArg, LoadOwnershipQualifier::Unqualified); } else { loadArg = argBuilder.createLoad(newArg->getLoc(), newArg, LoadOwnershipQualifier::Take); } newArg->replaceAllUsesWith(loadArg); loadArg->setOperand(newArg); // If the load is of a function type - do not replace it. if (isFuncOrOptionalFuncType(loadArg->getType())) { continue; } allocator.replaceLoad(loadArg); } caseBBs.push_back(std::make_pair(decl, currBB)); } SILBasicBlock *defaultBB = instr->hasDefault() ? instr->getDefaultBB() : nullptr; enumBuilder.createSwitchEnumAddr( instr->getLoc(), copiedValue, defaultBB, caseBBs); instr->getParent()->erase(instr); } while (!pass.structExtractInstsToMod.empty()) { auto *instr = pass.structExtractInstsToMod.pop_back_val(); createResultTyInstrAndLoad(allocator, instr, pass); } while (!pass.applies.empty()) { auto *applyInst = pass.applies.pop_back_val(); if (currentModApplies.count(applyInst) == 0) { currentModApplies.insert(applyInst); } ApplySite applySite = ApplySite(applyInst); allocateAndSetAll(pass, allocator, applyInst, applySite.getArgumentOperands()); } while (!pass.modYieldInsts.empty()) { YieldInst *inst = pass.modYieldInsts.pop_back_val(); allocateAndSetAll(pass, allocator, inst, inst->getAllOperands()); } repeat = !pass.switchEnumInstsToMod.empty() || !pass.structExtractInstsToMod.empty(); assert(pass.applies.empty()); pass.applies.append(currentModApplies.begin(), currentModApplies.end()); } while (repeat); for (SILInstruction *instr : pass.instsToMod) { for (Operand &operand : instr->getAllOperands()) { auto currOperand = operand.get(); if (std::find(pass.largeLoadableArgs.begin(), pass.largeLoadableArgs.end(), currOperand) != pass.largeLoadableArgs.end()) { SILValue newOperand = pass.argsToLoadedValueMap[currOperand]; assert(newOperand != currOperand && "Did not allocate storage and convert operand"); operand.set(newOperand); } } } for (SingleValueInstruction *instr : pass.tupleInstsToMod) { retypeTupleInstr(instr, pass.Mod, pass.Mapper); } while (!pass.allocStackInstsToMod.empty()) { auto *instr = pass.allocStackInstsToMod.pop_back_val(); SILBuilderWithScope allocBuilder(instr); SILType currSILType = instr->getType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), currSILType); auto *newInstr = allocBuilder.createAllocStack(instr->getLoc(), newSILType, instr->getVarInfo()); instr->replaceAllUsesWith(newInstr); instr->getParent()->erase(instr); } while (!pass.pointerToAddrkInstsToMod.empty()) { auto *instr = pass.pointerToAddrkInstsToMod.pop_back_val(); SILBuilderWithScope pointerBuilder(instr); SILType currSILType = instr->getType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), currSILType); auto *newInstr = pointerBuilder.createPointerToAddress( instr->getLoc(), instr->getOperand(), newSILType.getAddressType(), instr->isStrict(), instr->isInvariant(), instr->alignment()); instr->replaceAllUsesWith(newInstr); instr->getParent()->erase(instr); } for (DebugValueInst *instr : pass.debugInstsToMod) { assert(instr->getAllOperands().size() == 1 && "Debug instructions have one operand"); for (Operand &operand : instr->getAllOperands()) { auto currOperand = operand.get(); if (pass.argsToLoadedValueMap.find(currOperand) != pass.argsToLoadedValueMap.end()) { SILValue newOperand = pass.argsToLoadedValueMap[currOperand]; assert(newOperand != currOperand && "Did not allocate storage and convert operand"); operand.set(newOperand); } else { assert(currOperand->getType().isAddress() && "Expected an address type"); // SILBuilderWithScope skips over metainstructions. SILBuilder debugBuilder(instr, instr->getDebugScope()); debugBuilder.createDebugValueAddr(instr->getLoc(), currOperand, *instr->getVarInfo()); instr->getParent()->erase(instr); } } } for (SILInstruction *instr : pass.destroyValueInstsToMod) { assert(instr->getAllOperands().size() == 1 && "destroy_value instructions have one operand"); for (Operand &operand : instr->getAllOperands()) { auto currOperand = operand.get(); assert(currOperand->getType().isAddress() && "Expected an address type"); SILBuilderWithScope destroyBuilder(instr); destroyBuilder.createDestroyAddr(instr->getLoc(), currOperand); instr->getParent()->erase(instr); } } for (StoreInst *instr : pass.storeInstsToMod) { SILValue src = instr->getSrc(); SILValue tgt = instr->getDest(); SILType srcType = src->getType(); SILType tgtType = tgt->getType(); assert(srcType && "Expected an address-type source"); assert(tgtType.isAddress() && "Expected an address-type target"); assert(srcType == tgtType && "Source and target type do not match"); (void)srcType; (void)tgtType; SILBuilderWithScope copyBuilder(instr); createOutlinedCopyCall(copyBuilder, src, tgt, pass); instr->getParent()->erase(instr); } for (RetainValueInst *instr : pass.retainInstsToMod) { SILBuilderWithScope retainBuilder(instr); retainBuilder.createRetainValueAddr( instr->getLoc(), instr->getOperand(), instr->getAtomicity()); instr->getParent()->erase(instr); } for (ReleaseValueInst *instr : pass.releaseInstsToMod) { SILBuilderWithScope releaseBuilder(instr); releaseBuilder.createReleaseValueAddr( instr->getLoc(), instr->getOperand(), instr->getAtomicity()); instr->getParent()->erase(instr); } for (SingleValueInstruction *instr : pass.resultTyInstsToMod) { // Update the return type of these instrs // Note: The operand was already updated! SILType currSILType = instr->getType().getObjectType(); SILType newSILType = pass.getNewSILType(pass.F->getLoweredFunctionType(), currSILType); SILBuilderWithScope resultTyBuilder(instr); SILLocation Loc = instr->getLoc(); SingleValueInstruction *newInstr = nullptr; switch (instr->getKind()) { case SILInstructionKind::StructExtractInst: { auto *convInstr = cast(instr); newInstr = resultTyBuilder.createStructExtract( Loc, convInstr->getOperand(), convInstr->getField(), newSILType.getObjectType()); break; } case SILInstructionKind::StructElementAddrInst: { auto *convInstr = cast(instr); newInstr = resultTyBuilder.createStructElementAddr( Loc, convInstr->getOperand(), convInstr->getField(), newSILType.getAddressType()); break; } case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { auto *convInstr = cast(instr); newInstr = resultTyBuilder.createUncheckedTakeEnumDataAddr( Loc, convInstr->getOperand(), convInstr->getElement(), newSILType.getAddressType()); break; } case SILInstructionKind::InitEnumDataAddrInst: { auto *convInstr = cast(instr); newInstr = resultTyBuilder.createInitEnumDataAddr( Loc, convInstr->getOperand(), convInstr->getElement(), newSILType.getAddressType()); break; } case SILInstructionKind::RefTailAddrInst: { auto *convInstr = cast(instr); newInstr = resultTyBuilder.createRefTailAddr(Loc, convInstr->getOperand(), newSILType.getAddressType()); break; } case SILInstructionKind::RefElementAddrInst: { auto *convInstr = cast(instr); newInstr = resultTyBuilder.createRefElementAddr( Loc, convInstr->getOperand(), convInstr->getField(), newSILType.getAddressType()); break; } case SILInstructionKind::BeginAccessInst: { auto *convInstr = cast(instr); newInstr = resultTyBuilder.createBeginAccess( Loc, convInstr->getOperand(), convInstr->getAccessKind(), convInstr->getEnforcement(), convInstr->hasNoNestedConflict(), convInstr->isFromBuiltin()); break; } case SILInstructionKind::EnumInst: { auto *convInstr = cast(instr); SILValue operand = convInstr->hasOperand() ? convInstr->getOperand() : SILValue(); newInstr = resultTyBuilder.createEnum( Loc, operand, convInstr->getElement(), newSILType.getObjectType()); break; } default: llvm_unreachable("Unhandled aggrTy instr"); } instr->replaceAllUsesWith(newInstr); instr->eraseFromParent(); } for (MethodInst *instr : pass.methodInstsToMod) { SILType currSILType = instr->getType(); auto currSILFunctionType = currSILType.castTo(); GenericEnvironment *genEnvForMethod = nullptr; if (currSILFunctionType->isPolymorphic()) { genEnvForMethod = getSubstGenericEnvironment(currSILFunctionType); } SILType newSILType = SILType::getPrimitiveObjectType(pass.Mapper.getNewSILFunctionType( genEnvForMethod, currSILFunctionType, pass.Mod)); auto member = instr->getMember(); auto loc = instr->getLoc(); SILBuilderWithScope methodBuilder(instr); MethodInst *newInstr = nullptr; switch (instr->getKind()) { case SILInstructionKind::ClassMethodInst: { SILValue selfValue = instr->getOperand(0); newInstr = methodBuilder.createClassMethod(loc, selfValue, member, newSILType); break; } case SILInstructionKind::SuperMethodInst: { SILValue selfValue = instr->getOperand(0); newInstr = methodBuilder.createSuperMethod(loc, selfValue, member, newSILType); break; } case SILInstructionKind::WitnessMethodInst: { auto *WMI = cast(instr); newInstr = methodBuilder.createWitnessMethod( loc, WMI->getLookupType(), WMI->getConformance(), member, newSILType); break; } default: llvm_unreachable("Expected known MethodInst ValueKind"); } instr->replaceAllUsesWith(newInstr); instr->getParent()->erase(instr); } while (!pass.modReturnInsts.empty()) { auto *instr = pass.modReturnInsts.pop_back_val(); auto loc = instr->getLoc(); // SILLocation::RegularKind auto regLoc = RegularLocation(loc); SILBuilderWithScope retBuilder(instr); assert(modNonFuncTypeResultType(pass.F, pass.Mod) && "Expected a regular type"); // Before we return an empty tuple, init return arg: auto *entry = pass.F->getEntryBlock(); auto *retArg = entry->getArgument(0); auto retOp = instr->getOperand(); auto storageType = retOp->getType(); if (storageType.isAddress()) { // There *might* be a dealloc_stack that already released this value // we should create the copy *before* the epilogue's deallocations auto IIR = instr->getReverseIterator(); for (++IIR; IIR != instr->getParent()->rend(); ++IIR) { auto *currIIInstr = &(*IIR); if (currIIInstr->getKind() != SILInstructionKind::DeallocStackInst) { // got the right location - stop. --IIR; break; } } auto II = (IIR != instr->getParent()->rend()) ? IIR->getIterator() : instr->getParent()->begin(); SILBuilderWithScope retCopyBuilder(II); createOutlinedCopyCall(retCopyBuilder, retOp, retArg, pass, ®Loc); } else { retBuilder.createStore(regLoc, retOp, retArg, getStoreInitOwnership(pass, retOp->getType())); } auto emptyTy = SILType::getPrimitiveObjectType( retBuilder.getModule().getASTContext().TheEmptyTupleType); auto newRetTuple = retBuilder.createTuple(regLoc, emptyTy, {}); retBuilder.createReturn(newRetTuple->getLoc(), newRetTuple); instr->eraseFromParent(); } } // Rewrite function return argument if it is a "function pointer" // If it is a large type also return true - will be re-written later // Returns true if the return argument needed re-writing static bool rewriteFunctionReturn(StructLoweringState &pass) { auto loweredTy = pass.F->getLoweredFunctionType(); SILFunction *F = pass.F; SILType resultTy = loweredTy->getAllResultsInterfaceType(); SILType newSILType = pass.getNewSILType(loweredTy, resultTy); // We (currently) only care about function signatures if (pass.isLargeLoadableType(loweredTy, resultTy)) { return true; } else if (pass.containsDifferentFunctionSignature(loweredTy, resultTy)) { llvm::SmallVector newSILResultInfo; if (auto tupleType = newSILType.getAs()) { auto originalResults = loweredTy->getResults(); for (unsigned int i = 0; i < originalResults.size(); ++i) { auto origResultInfo = originalResults[i]; auto canElem = tupleType.getElementType(i); SILType objectType = SILType::getPrimitiveObjectType(canElem); auto newResult = SILResultInfo(objectType.getASTType(), origResultInfo.getConvention()); newSILResultInfo.push_back(newResult); } } else { assert(loweredTy->getNumResults() == 1 && "Expected a single result"); auto origResultInfo = loweredTy->getSingleResult(); auto newResult = SILResultInfo(newSILType.getASTType(), origResultInfo.getConvention()); newSILResultInfo.push_back(newResult); } auto NewTy = SILFunctionType::get( loweredTy->getInvocationGenericSignature(), loweredTy->getExtInfo(), loweredTy->getCoroutineKind(), loweredTy->getCalleeConvention(), loweredTy->getParameters(), loweredTy->getYields(), newSILResultInfo, loweredTy->getOptionalErrorResult(), loweredTy->getPatternSubstitutions(), loweredTy->getInvocationSubstitutions(), F->getModule().getASTContext(), loweredTy->getWitnessMethodConformanceOrInvalid()); F->rewriteLoweredTypeUnsafe(NewTy); return true; } return false; } void LoadableByAddress::runOnFunction(SILFunction *F) { CanSILFunctionType funcType = F->getLoweredFunctionType(); IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule(F); if (F->isExternalDeclaration()) { if (!modifiableFunction(funcType)) { return; } // External function - re-write external declaration - this is ABI! GenericEnvironment *genEnv = getSubstGenericEnvironment(F); auto loweredTy = F->getLoweredFunctionType(); if (!genEnv && loweredTy->getSubstGenericSignature()) { genEnv = getSubstGenericEnvironment(loweredTy); } if (MapperCache.shouldTransformFunctionType(genEnv, loweredTy, *currIRMod)) { modFuncs.insert(F); } return; } StructLoweringState pass(F, *currIRMod, MapperCache); // Rewrite function args and insert allocs. LoadableStorageAllocation allocator(pass); allocator.allocateLoadableStorage(); bool rewrittenReturn = false; if (modifiableFunction(funcType)) { rewrittenReturn = rewriteFunctionReturn(pass); } LLVM_DEBUG(llvm::dbgs() << "\nREWRITING: " << F->getName(); F->print(llvm::dbgs())); // Rewrite instructions relating to the loadable struct. rewriteFunction(pass, allocator); invalidateAnalysis(F, SILAnalysis::InvalidationKind::Instructions); // If we modified the function arguments - add to list of functions to clone if (modifiableFunction(funcType) && (rewrittenReturn || !pass.largeLoadableArgs.empty() || !pass.funcSigArgs.empty() || pass.hasLargeLoadableYields())) { modFuncs.insert(F); } // If we modified any applies - add them to the global list for recreation if (!pass.applies.empty()) { modApplies.insert(pass.applies.begin(), pass.applies.end()); } if (!pass.applyRetToAllocMap.empty()) { for (auto elm : pass.applyRetToAllocMap) { allApplyRetToAllocMap.insert(elm); } } } static SILValue getOperandTypeWithCastIfNecessary(SILInstruction *containingInstr, SILValue op, IRGenModule &Mod, SILBuilder &builder, LargeSILTypeMapper &Mapper) { SILType currSILType = op->getType(); SILType nonOptionalType = currSILType; if (auto optType = currSILType.getOptionalObjectType()) { nonOptionalType = optType; } if (auto funcType = nonOptionalType.getAs()) { GenericEnvironment *genEnv = getSubstGenericEnvironment(containingInstr->getFunction()); if (!genEnv && funcType->isPolymorphic()) { genEnv = getSubstGenericEnvironment(funcType); } auto newFnType = Mapper.getNewSILFunctionType(genEnv, funcType, Mod); SILType newSILType = SILType::getPrimitiveObjectType(newFnType); if (nonOptionalType.isAddress()) { newSILType = newSILType.getAddressType(); } if (nonOptionalType != currSILType) { newSILType = SILType::getOptionalType(newSILType); } if (currSILType.isAddress()) { newSILType = newSILType.getAddressType(); } if (currSILType.isAddress()) { if (newSILType != currSILType) { auto castInstr = builder.createUncheckedAddrCast( containingInstr->getLoc(), op, newSILType); return castInstr; } return op; } assert(currSILType.isObject() && "Expected an object type"); if (newSILType != currSILType) { auto castInstr = builder.createUncheckedReinterpretCast( containingInstr->getLoc(), op, newSILType); return castInstr; } } return op; } void LoadableByAddress::recreateSingleApply( SILInstruction *applyInst, SmallVectorImpl &Delete) { auto *F = applyInst->getFunction(); IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule(F); // Collect common info ApplySite applySite = ApplySite(applyInst); SILValue callee = applySite.getCallee(); if (auto site = ApplySite::isa(callee)) { // We need to re-create the callee's apply before recreating this one // else verification will fail with wrong SubstCalleeType auto calleInstr = site.getInstruction(); if (modApplies.remove(calleInstr)) { recreateSingleApply(calleInstr, Delete); callee = applySite.getCallee(); } } CanSILFunctionType origSILFunctionType = applySite.getSubstCalleeType(); GenericEnvironment *genEnv = getSubstGenericEnvironment(origSILFunctionType); CanSILFunctionType newSILFunctionType = MapperCache.getNewSILFunctionType( genEnv, origSILFunctionType, *currIRMod); SILFunctionConventions newSILFunctionConventions(newSILFunctionType, *getModule()); SmallVector callArgs; SILBuilderWithScope applyBuilder(applyInst); // If we turned a direct result into an indirect parameter // Find the new alloc we created earlier. // and pass it as first parameter: if ((isa(applyInst) || isa(applyInst)) && modNonFuncTypeResultType(genEnv, origSILFunctionType, *currIRMod) && modifiableApply(applySite, *getIRGenModule())) { assert(allApplyRetToAllocMap.find(applyInst) != allApplyRetToAllocMap.end()); auto newAlloc = allApplyRetToAllocMap.find(applyInst)->second; callArgs.push_back(newAlloc); } // Collect arg operands for (Operand &operand : applySite.getArgumentOperands()) { SILValue currOperand = operand.get(); currOperand = getOperandTypeWithCastIfNecessary( applyInst, currOperand, *currIRMod, applyBuilder, MapperCache); callArgs.push_back(currOperand); } // Recreate apply with new operands due to substitution-type cache switch (applyInst->getKind()) { case SILInstructionKind::ApplyInst: { auto *castedApply = cast(applyInst); SILValue newApply = applyBuilder.createApply(castedApply->getLoc(), callee, applySite.getSubstitutionMap(), callArgs, castedApply->getApplyOptions()); castedApply->replaceAllUsesWith(newApply); break; } case SILInstructionKind::TryApplyInst: { auto *castedApply = cast(applyInst); applyBuilder.createTryApply( castedApply->getLoc(), callee, applySite.getSubstitutionMap(), callArgs, castedApply->getNormalBB(), castedApply->getErrorBB(), castedApply->getApplyOptions()); break; } case SILInstructionKind::BeginApplyInst: { auto oldApply = cast(applyInst); auto newApply = applyBuilder.createBeginApply(oldApply->getLoc(), callee, applySite.getSubstitutionMap(), callArgs, oldApply->getApplyOptions()); // Use the new token result. oldApply->getTokenResult()->replaceAllUsesWith(newApply->getTokenResult()); if (auto *allocation = oldApply->getCalleeAllocationResult()) { allocation->replaceAllUsesWith(newApply->getCalleeAllocationResult()); } // Rewrite all the yields. auto oldYields = oldApply->getOrigCalleeType()->getYields(); auto oldYieldedValues = oldApply->getYieldedValues(); auto newYields = newApply->getOrigCalleeType()->getYields(); auto newYieldedValues = newApply->getYieldedValues(); assert(oldYields.size() == newYields.size() && oldYields.size() == oldYieldedValues.size() && newYields.size() == newYieldedValues.size()); (void)newYields; for (auto i : indices(oldYields)) { SILValue oldValue = oldYieldedValues[i]; SILValue newValue = newYieldedValues[i]; // For now, just replace the value with an immediate load if the old value // was direct. if (oldValue->getType() != newValue->getType() && !oldValue->getType().isAddress()) { LoadOwnershipQualifier ownership; if (!F->hasOwnership()) { ownership = LoadOwnershipQualifier::Unqualified; } else if (newValue->getType().isTrivial(*F)) { ownership = LoadOwnershipQualifier::Trivial; } else { assert(oldYields[i].isConsumedInCaller() && "borrowed yields not yet supported here"); ownership = LoadOwnershipQualifier::Take; } newValue = applyBuilder.createLoad(applyInst->getLoc(), newValue, ownership); } oldValue->replaceAllUsesWith(newValue); } break; } case SILInstructionKind::PartialApplyInst: { auto *castedApply = cast(applyInst); // Change the type of the Closure auto partialApplyConvention = castedApply->getCalleeConvention(); auto resultIsolation = castedApply->getResultIsolation(); // We do need to update the closure's funtion type to match with the other // uses inside of the binary. Pointer auth cares about the SIL function // type. if (callee->getType().castTo()->getExtInfo().getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod) { CanSILFunctionType newFnType = MapperCache.getNewSILFunctionType( genEnv, callee->getType().castTo(), *currIRMod, /*mustTransform*/ true); SILType newType = SILType::getPrimitiveObjectType(newFnType); callee = applyBuilder.createConvertFunction(castedApply->getLoc(), callee, newType, false); } auto newApply = applyBuilder.createPartialApply( castedApply->getLoc(), callee, applySite.getSubstitutionMap(), callArgs, partialApplyConvention, resultIsolation, castedApply->isOnStack()); castedApply->replaceAllUsesWith(newApply); break; } default: llvm_unreachable("Unexpected instr: unknown apply type"); } Delete.push_back(applyInst); } bool LoadableByAddress::recreateApply( SILInstruction &I, SmallVectorImpl &Delete) { if (!modApplies.count(&I)) return false; recreateSingleApply(&I, Delete); modApplies.remove(&I); return true; } bool LoadableByAddress::recreateLoadInstr( SILInstruction &I, SmallVectorImpl &Delete) { auto *loadInstr = dyn_cast(&I); if (!loadInstr || !loadInstrsOfFunc.count(loadInstr)) return false; SILBuilderWithScope loadBuilder(loadInstr); // If this is a load of a function for which we changed the return type: // add UncheckedBitCast before the load auto loadOp = loadInstr->getOperand(); loadOp = getOperandTypeWithCastIfNecessary( loadInstr, loadOp, *getIRGenModule(), loadBuilder, MapperCache); auto *newInstr = loadBuilder.createLoad(loadInstr->getLoc(), loadOp, loadInstr->getOwnershipQualifier()); loadInstr->replaceAllUsesWith(newInstr); Delete.push_back(loadInstr); return true; } bool LoadableByAddress::recreateUncheckedEnumDataInstr( SILInstruction &I, SmallVectorImpl &Delete) { auto enumInstr = dyn_cast(&I); if (!enumInstr || !uncheckedEnumDataOfFunc.count(enumInstr)) return false; SILBuilderWithScope enumBuilder(enumInstr); SILFunction *F = enumInstr->getFunction(); IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule(F); SILType origType = enumInstr->getType(); GenericEnvironment *genEnv = getSubstGenericEnvironment(F); SILType newType = MapperCache.getNewSILType(genEnv, origType, *currIRMod); auto caseTy = enumInstr->getOperand()->getType().getEnumElementType( enumInstr->getElement(), F->getModule(), TypeExpansionContext(*F)); SingleValueInstruction *newInstr = nullptr; if (newType.isAddress()) { newType = newType.getObjectType(); } if (caseTy != newType) { auto *takeEnum = enumBuilder.createUncheckedEnumData( enumInstr->getLoc(), enumInstr->getOperand(), enumInstr->getElement(), caseTy); newInstr = enumBuilder.createUncheckedReinterpretCast(enumInstr->getLoc(), takeEnum, newType); } else { newInstr = enumBuilder.createUncheckedEnumData( enumInstr->getLoc(), enumInstr->getOperand(), enumInstr->getElement(), newType); } enumInstr->replaceAllUsesWith(newInstr); Delete.push_back(enumInstr); return false; } bool LoadableByAddress::recreateUncheckedTakeEnumDataAddrInst( SILInstruction &I, SmallVectorImpl &Delete) { auto *enumInstr = dyn_cast(&I); if (!enumInstr || !uncheckedTakeEnumDataAddrOfFunc.count(enumInstr)) return false; SILBuilderWithScope enumBuilder(enumInstr); SILFunction *F = enumInstr->getFunction(); IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule(F); SILType origType = enumInstr->getType(); GenericEnvironment *genEnv = getSubstGenericEnvironment(F); SILType newType = MapperCache.getNewSILType(genEnv, origType, *currIRMod); auto caseTy = enumInstr->getOperand()->getType().getEnumElementType( enumInstr->getElement(), F->getModule(), TypeExpansionContext(*F)); SingleValueInstruction *newInstr = nullptr; if (caseTy != origType.getObjectType()) { auto *takeEnum = enumBuilder.createUncheckedTakeEnumDataAddr( enumInstr->getLoc(), enumInstr->getOperand(), enumInstr->getElement(), caseTy.getAddressType()); newInstr = enumBuilder.createUncheckedAddrCast( enumInstr->getLoc(), takeEnum, newType.getAddressType()); } else { newInstr = enumBuilder.createUncheckedTakeEnumDataAddr( enumInstr->getLoc(), enumInstr->getOperand(), enumInstr->getElement(), newType.getAddressType()); } enumInstr->replaceAllUsesWith(newInstr); Delete.push_back(enumInstr); return true; } bool LoadableByAddress::fixStoreToBlockStorageInstr( SILInstruction &I, SmallVectorImpl &Delete) { auto *instr = dyn_cast(&I); if (!instr || !storeToBlockStorageInstrs.count(instr)) return false; auto dest = instr->getDest(); auto destBlock = cast(dest); SILType destType = destBlock->getType(); auto src = instr->getSrc(); SILType srcType = src->getType(); if (destType.getObjectType() != srcType) { // Add cast to destType SILBuilderWithScope castBuilder(instr); auto *castInstr = castBuilder.createUncheckedReinterpretCast( instr->getLoc(), src, destType.getObjectType()); instr->setOperand(StoreInst::Src, castInstr); } return true; } bool LoadableByAddress::recreateDifferentiabilityWitnessFunction( SILInstruction &I, SmallVectorImpl &Delete) { auto *instr = dyn_cast(&I); if (!instr) return false; // Check if we need to recreate the instruction. auto *currIRMod = getIRGenModule()->IRGen.getGenModule(instr->getFunction()); auto resultFnTy = instr->getType().castTo(); auto genSig = resultFnTy->getSubstGenericSignature(); auto *genEnv = genSig.getGenericEnvironment(); auto newResultFnTy = MapperCache.getNewSILFunctionType(genEnv, resultFnTy, *currIRMod); if (resultFnTy == newResultFnTy) return true; SILBuilderWithScope builder(instr); auto *newInstr = builder.createDifferentiabilityWitnessFunction( instr->getLoc(), instr->getWitnessKind(), instr->getWitness(), SILType::getPrimitiveObjectType(newResultFnTy)); instr->replaceAllUsesWith(newInstr); Delete.push_back(instr); return true; } bool LoadableByAddress::recreateTupleInstr( SILInstruction &I, SmallVectorImpl &Delete) { auto *tupleInstr = dyn_cast(&I); if (!tupleInstr) return false; // Check if we need to recreate the tuple: auto *F = tupleInstr->getFunction(); auto *currIRMod = getIRGenModule()->IRGen.getGenModule(F); GenericEnvironment *genEnv = getSubstGenericEnvironment(F); auto resultTy = tupleInstr->getType(); auto newResultTy = MapperCache.getNewSILType(genEnv, resultTy, *currIRMod); if (resultTy == newResultTy) return true; // The tuple type have changed based on its members. // For example if one or more of them are ‘large’ loadable types SILBuilderWithScope tupleBuilder(tupleInstr); SmallVector elems; for (auto elem : tupleInstr->getElements()) { elems.push_back(elem); } auto *newTuple = tupleBuilder.createTuple(tupleInstr->getLoc(), newResultTy, elems); tupleInstr->replaceAllUsesWith(newTuple); Delete.push_back(tupleInstr); return true; } bool LoadableByAddress::recreateConvInstr(SILInstruction &I, SmallVectorImpl &Delete) { auto *convInstr = dyn_cast(&I); if (!convInstr || !conversionInstrs.count(convInstr)) return false; IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule(convInstr->getFunction()); SILType currSILType = convInstr->getType(); auto currSILFunctionType = currSILType.castTo(); GenericEnvironment *genEnv = getSubstGenericEnvironment(convInstr->getFunction()); // Differentiable function conversion instructions can happen while the // function is still generic. In that case, we must calculate the new type // using the converted function's generic environment rather than the // converting function's generic environment. // // This happens in witness thunks for default implementations of derivative // requirements. if (convInstr->getKind() == SILInstructionKind::DifferentiableFunctionInst || convInstr->getKind() == SILInstructionKind::DifferentiableFunctionExtractInst || convInstr->getKind() == SILInstructionKind::LinearFunctionInst || convInstr->getKind() == SILInstructionKind::LinearFunctionExtractInst) { genEnv = currSILFunctionType->getSubstGenericSignature().getGenericEnvironment(); } CanSILFunctionType newFnType = MapperCache.getNewSILFunctionType( genEnv, currSILFunctionType, *currIRMod); SILType newType = SILType::getPrimitiveObjectType(newFnType); SILBuilderWithScope convBuilder(convInstr); SingleValueInstruction *newInstr = nullptr; switch (convInstr->getKind()) { case SILInstructionKind::ThinToThickFunctionInst: { auto instr = cast(convInstr); newInstr = convBuilder.createThinToThickFunction( instr->getLoc(), instr->getOperand(), newType); break; } case SILInstructionKind::ConvertFunctionInst: { auto instr = cast(convInstr); newInstr = convBuilder.createConvertFunction( instr->getLoc(), instr->getOperand(), newType, instr->withoutActuallyEscaping()); break; } case SILInstructionKind::ConvertEscapeToNoEscapeInst: { auto instr = cast(convInstr); newInstr = convBuilder.createConvertEscapeToNoEscape( instr->getLoc(), instr->getOperand(), newType, instr->isLifetimeGuaranteed()); break; } case SILInstructionKind::MarkDependenceInst: { auto instr = cast(convInstr); newInstr = convBuilder.createMarkDependence( instr->getLoc(), instr->getValue(), instr->getBase(), instr->dependenceKind()); break; } case SILInstructionKind::DifferentiableFunctionInst: { auto instr = cast(convInstr); newInstr = convBuilder.createDifferentiableFunction( instr->getLoc(), instr->getParameterIndices(), instr->getResultIndices(), instr->getOriginalFunction(), instr->getOptionalDerivativeFunctionPair()); break; } case SILInstructionKind::DifferentiableFunctionExtractInst: { auto instr = cast(convInstr); // Rewrite `differentiable_function_extract` with explicit extractee type. newInstr = convBuilder.createDifferentiableFunctionExtract( instr->getLoc(), instr->getExtractee(), instr->getOperand(), newType); break; } case SILInstructionKind::LinearFunctionInst: { auto instr = cast(convInstr); newInstr = convBuilder.createLinearFunction( instr->getLoc(), instr->getParameterIndices(), instr->getOriginalFunction(), instr->getOptionalTransposeFunction()); break; } case SILInstructionKind::LinearFunctionExtractInst: { auto instr = cast(convInstr); newInstr = convBuilder.createLinearFunctionExtract( instr->getLoc(), instr->getExtractee(), instr->getOperand()); break; } default: llvm_unreachable("Unexpected conversion instruction"); } convInstr->replaceAllUsesWith(newInstr); Delete.push_back(convInstr); return true; } bool LoadableByAddress::recreateBuiltinInstr(SILInstruction &I, SmallVectorImpl &Delete) { auto builtinInstr = dyn_cast(&I); if (!builtinInstr || !builtinInstrs.count(builtinInstr)) return false; auto *currIRMod = getIRGenModule()->IRGen.getGenModule(builtinInstr->getFunction()); auto *F = builtinInstr->getFunction(); GenericEnvironment *genEnv = getSubstGenericEnvironment(F); auto resultTy = builtinInstr->getType(); auto newResultTy = MapperCache.getNewSILType(genEnv, resultTy, *currIRMod); llvm::SmallVector newArgs; for (auto oldArg : builtinInstr->getArguments()) { newArgs.push_back(oldArg); } SILBuilderWithScope builtinBuilder(builtinInstr); auto *newInstr = builtinBuilder.createBuiltin( builtinInstr->getLoc(), builtinInstr->getName(), newResultTy, builtinInstr->getSubstitutions(), newArgs); builtinInstr->replaceAllUsesWith(newInstr); Delete.push_back(builtinInstr); return true; } void LoadableByAddress::updateLoweredTypes(SILFunction *F) { IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule(F); CanSILFunctionType funcType = F->getLoweredFunctionType(); GenericEnvironment *genEnv = getSubstGenericEnvironment(F); if (!genEnv && funcType->getSubstGenericSignature()) { genEnv = getSubstGenericEnvironment(funcType); } auto newFuncTy = MapperCache.getNewSILFunctionType(genEnv, funcType, *currIRMod); F->rewriteLoweredTypeUnsafe(newFuncTy); } void IRGenModule::lowerSILFunction(SILFunction *f) { CanSILFunctionType funcType = f->getLoweredFunctionType(); GenericEnvironment *genEnv = getSubstGenericEnvironment(f); if (!genEnv && funcType->getSubstGenericSignature()) { genEnv = getSubstGenericEnvironment(funcType); } LargeSILTypeMapper MapperCache; auto newFuncTy = MapperCache.getNewSILFunctionType(genEnv, funcType, *this); f->rewriteLoweredTypeUnsafe(newFuncTy); } bool LoadableByAddress::shouldTransformGlobal(SILGlobalVariable *global) { SILInstruction *init = global->getStaticInitializerValue(); if (!init) return false; auto silTy = global->getLoweredType(); if (!isa(silTy.getASTType())) return false; auto *decl = global->getDecl(); IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule( decl ? decl->getDeclContext() : nullptr); auto silFnTy = global->getLoweredFunctionType(); GenericEnvironment *genEnv = getSubstGenericEnvironment(silFnTy); if (MapperCache.shouldTransformFunctionType(genEnv, silFnTy, *currIRMod)) return true; return false; } bool LoadableByAddress::shouldTransformInitExprOfGlobal(SILGlobalVariable *global) { for (const SILInstruction &initInst : *global) { if (auto *fri = dyn_cast(&initInst)) { SILFunction *refF = fri->getInitiallyReferencedFunction(); if (modFuncs.count(refF) != 0) return true; } } return false; } namespace { class GlobalInitCloner : public SILCloner { LoadableByAddress *pass; IRGenModule *irgenModule; public: GlobalInitCloner(SILGlobalVariable *global, LoadableByAddress *pass, IRGenModule *irgenModule) : SILCloner(global), pass(pass), irgenModule(irgenModule) { } SILType remapType(SILType ty) { ty = ty.subst(getBuilder().getModule(), Functor, Functor); if (auto fnType = ty.getAs()) { GenericEnvironment *genEnv = getSubstGenericEnvironment(fnType); return SILType::getPrimitiveObjectType( pass->MapperCache.getNewSILFunctionType(genEnv, fnType, *irgenModule)); } return ty; } void clone(SILInstruction *inst) { visit(inst); } }; } static void runPeepholesAndReg2Mem(SILPassManager *pm, SILModule *silMod, IRGenModule *irgenModule); /// The entry point to this function transformation. void LoadableByAddress::run() { // Set the SIL state before the PassManager has a chance to run // verification. getModule()->setStage(SILStage::Lowered); for (auto &F : *getModule()) runOnFunction(&F); if (modFuncs.empty() && modApplies.empty()) { runPeepholesAndReg2Mem(getPassManager(), getModule(), getIRGenModule()); return; } // Scan the module for all references of the modified functions: llvm::SetVector funcRefs; llvm::SetVector globalRefs; for (SILFunction &CurrF : *getModule()) { for (SILBasicBlock &BB : CurrF) { for (SILInstruction &I : BB) { if (auto *allocGlobal = dyn_cast(&I)) { auto *global = allocGlobal->getReferencedGlobal(); if (shouldTransformGlobal(global)) globalRefs.insert(allocGlobal); } else if (auto *globalAddr = dyn_cast(&I)) { auto *global = globalAddr->getReferencedGlobal(); if (shouldTransformGlobal(global)) globalRefs.insert(globalAddr); } else if (auto *globalVal = dyn_cast(&I)) { auto *global = globalVal->getReferencedGlobal(); if (shouldTransformGlobal(global)) globalRefs.insert(globalVal); } else if (auto *FRI = dyn_cast(&I)) { SILFunction *RefF = FRI->getInitiallyReferencedFunction(); if (modFuncs.count(RefF) != 0) { // Go over the uses and add them to lists to modify // // FIXME: Why aren't function_ref uses processed transitively? And // why is it necessary to visit uses at all if they will be visited // later in this loop? for (auto *user : FRI->getUses()) { SILInstruction *currInstr = user->getUser(); switch (currInstr->getKind()) { case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::PartialApplyInst: { if (modApplies.count(currInstr) == 0) { modApplies.insert(currInstr); } break; } case SILInstructionKind::ConvertFunctionInst: case SILInstructionKind::ConvertEscapeToNoEscapeInst: case SILInstructionKind::MarkDependenceInst: case SILInstructionKind::ThinToThickFunctionInst: case SILInstructionKind::DifferentiableFunctionInst: case SILInstructionKind::LinearFunctionInst: case SILInstructionKind::LinearFunctionExtractInst: case SILInstructionKind::DifferentiableFunctionExtractInst: { conversionInstrs.insert( cast(currInstr)); break; } case SILInstructionKind::BuiltinInst: { auto *instr = cast(currInstr); builtinInstrs.insert(instr); break; } case SILInstructionKind::BranchInst: case SILInstructionKind::StructInst: case SILInstructionKind::DebugValueInst: break; default: #ifndef NDEBUG currInstr->dump(); currInstr->getFunction()->dump(); #endif llvm_unreachable("Unhandled use of FunctionRefInst"); } } funcRefs.insert(FRI); } } else if (auto *Cvt = dyn_cast(&I)) { SILValue val = Cvt->getValue(); SILType currType = val->getType(); if (auto fType = currType.getAs()) { if (modifiableFunction(fType)) { conversionInstrs.insert(Cvt); } } } else if (auto *Cvt = dyn_cast(&I)) { SILValue val = Cvt->getOperand(); SILType currType = val->getType(); auto fType = currType.getAs(); assert(fType && "Expected SILFunctionType"); if (modifiableFunction(fType)) { conversionInstrs.insert(Cvt); } } else if (auto *CFI = dyn_cast(&I)) { SILValue val = CFI->getOperand(); SILType currType = val->getType(); auto fType = currType.getAs(); assert(fType && "Expected SILFunctionType"); if (modifiableFunction(fType)) { conversionInstrs.insert(CFI); } } else if (auto *TTI = dyn_cast(&I)) { auto canType = TTI->getCallee()->getType(); auto fType = canType.castTo(); if (modifiableFunction(fType)) conversionInstrs.insert(TTI); } else if (auto *LI = dyn_cast(&I)) { loadInstrsOfFunc.insert(LI); } else if (auto *UED = dyn_cast(&I)) { uncheckedEnumDataOfFunc.insert(UED); } else if (auto *UED = dyn_cast(&I)) { uncheckedTakeEnumDataAddrOfFunc.insert(UED); } else if (auto *SI = dyn_cast(&I)) { auto dest = SI->getDest(); if (isa(dest)) { storeToBlockStorageInstrs.insert(SI); } } else if (auto *PAI = dyn_cast(&I)) { if (modApplies.count(PAI) == 0) { modApplies.insert(PAI); } } else if (isa(&I) || isa(&I) || isa(&I) || isa(&I)) { conversionInstrs.insert(cast(&I)); } } } } for (auto *F : modFuncs) { // Update the lowered type of the Function updateLoweredTypes(F); } // Update globals' initializer. SmallVector deadGlobals; for (SILGlobalVariable &global : getModule()->getSILGlobals()) { if (shouldTransformInitExprOfGlobal(&global)) { auto *decl = global.getDecl(); IRGenModule *currIRMod = getIRGenModule()->IRGen.getGenModule( decl ? decl->getDeclContext() : nullptr); auto silTy = global.getLoweredType(); if (isa(silTy.getASTType())) { auto silFnTy = global.getLoweredFunctionType(); GenericEnvironment *genEnv = getSubstGenericEnvironment(silFnTy); if (MapperCache.shouldTransformFunctionType(genEnv, silFnTy, *currIRMod)) { auto newSILFnType = MapperCache.getNewSILFunctionType(genEnv, silFnTy, *currIRMod); global.unsafeSetLoweredType(SILType::getPrimitiveObjectType(newSILFnType)); } } // Rewrite the init basic block... SmallVector initBlockInsts; for (auto it = global.begin(), end = global.end(); it != end; ++it) { initBlockInsts.push_back(const_cast(&*it)); } GlobalInitCloner cloner(&global, this, currIRMod); for (auto *oldInst : initBlockInsts) { cloner.clone(oldInst); global.unsafeRemove(oldInst, *getModule()); } } } // Rewrite global variable users. for (auto *inst : globalRefs) { if (auto *allocGlobal = dyn_cast(inst)) { // alloc_global produces no results. SILBuilderWithScope builder(inst); builder.createAllocGlobal(allocGlobal->getLoc(), allocGlobal->getReferencedGlobal()); allocGlobal->eraseFromParent(); } else if (auto *globalAddr = dyn_cast(inst)) { SILBuilderWithScope builder(inst); auto *newInst = builder.createGlobalAddr( globalAddr->getLoc(), globalAddr->getReferencedGlobal(), globalAddr->getDependencyToken()); globalAddr->replaceAllUsesWith(newInst); globalAddr->eraseFromParent(); } else if (auto *globalVal = dyn_cast(inst)) { SILBuilderWithScope builder(inst); auto *newInst = builder.createGlobalValue( globalVal->getLoc(), globalVal->getReferencedGlobal(), globalVal->isBare()); globalVal->replaceAllUsesWith(newInst); globalVal->eraseFromParent(); } } // Update all references: // Note: We don't need to update the witness tables and vtables // They just contain a pointer to the function // The pointer does not change for (auto *instr : funcRefs) { SILFunction *F = instr->getInitiallyReferencedFunction(); SILBuilderWithScope refBuilder(instr); SingleValueInstruction *newInstr = refBuilder.createFunctionRef(instr->getLoc(), F, instr->getKind()); instr->replaceAllUsesWith(newInstr); instr->getParent()->erase(instr); } // Recreate the instructions in topological order. Some instructions inherit // their result type from their operand. for (SILFunction &CurrF : *getModule()) { SmallVector Delete; for (SILBasicBlock &BB : CurrF) { for (SILInstruction &I : BB) { if (recreateTupleInstr(I, Delete)) continue; else if (recreateConvInstr(I, Delete)) continue; else if (recreateBuiltinInstr(I, Delete)) continue; else if (recreateUncheckedEnumDataInstr(I, Delete)) continue; else if (recreateUncheckedTakeEnumDataAddrInst(I, Delete)) continue; else if (recreateLoadInstr(I, Delete)) continue; else if (recreateApply(I, Delete)) continue; else if (recreateDifferentiabilityWitnessFunction(I, Delete)) continue; else fixStoreToBlockStorageInstr(I, Delete); } } for (auto *Inst : Delete) Inst->eraseFromParent(); } // Clean up the data structs: modFuncs.clear(); conversionInstrs.clear(); loadInstrsOfFunc.clear(); uncheckedEnumDataOfFunc.clear(); modApplies.clear(); storeToBlockStorageInstrs.clear(); getPassManager()->invalidateAllAnalysis(); runPeepholesAndReg2Mem(getPassManager(), getModule(), getIRGenModule()); } namespace { struct Peepholes { SmallVector Delete; llvm::DenseSet Ignore; SILPassManager *pm; SILModule *silMod; IRGenModule *irgenModule; Peepholes(SILPassManager *pm, SILModule *silMod, IRGenModule *irgenModule) : pm(pm), silMod(silMod), irgenModule(irgenModule) {} void markForDeletion(SILInstruction *i) { Delete.push_back(i); Ignore.insert(i); } bool ignore(SILInstruction *i) { return Ignore.count(i); } void deleteInstructions() { for (auto *inst : Delete) { inst->eraseFromParent(); } } bool optimizeLoad(SILBasicBlock &BB, SILInstruction *I); }; } // namespace bool Peepholes::optimizeLoad(SILBasicBlock &BB, SILInstruction *I) { assert(!ignore(I)); auto *LI = dyn_cast(I); if (!LI) return false; auto nextIt = std::next(SILBasicBlock::iterator(LI)); if (nextIt == BB.end()) return false; // %4 = load %3 : $*LargeThing // retain_value %4 : $LargeThing // store %4 to %0 : $*LargeThing // -> // copy_addr %3 to [init] %0uto nextIt = // std::next(SILBasicBlock::iterator(LI)); if (auto *retain = dyn_cast(&*nextIt)) { if (!LI->hasTwoUses()) return false; if (retain->getOperand() != LI) return false; if (ignore(retain)) return false; auto next2It = std::next(nextIt); if (next2It == BB.end()) return false; auto *store = dyn_cast(&*next2It); if (!store || store->getSrc() != LI) return false; if (ignore(store)) return false; SILBuilderWithScope builder(LI); builder.createCopyAddr(store->getLoc(), LI->getOperand(), store->getDest(), IsNotTake, IsInitialization); markForDeletion(store); markForDeletion(retain); markForDeletion(LI); return true; } // %5 = load %4 : $*LargeThing // copy_addr [take] %0 to [initialization] %4 : $*LargeThing // release_value %5 : $LargeThing // -> // copy_addr [take] %0 to %4 if (auto *copyAddr = dyn_cast(&*nextIt)) { if (copyAddr->getDest() != LI->getOperand() || !copyAddr->isTakeOfSrc() || !copyAddr->isInitializationOfDest() || ignore(copyAddr)) return false; if (!LI->hasOneUse()) return false; auto next2It = std::next(nextIt); if (next2It == BB.end()) return false; auto *release = dyn_cast(&*next2It); if (!release || release->getOperand() != LI || ignore(release)) return false; SILBuilderWithScope builder(LI); builder.createCopyAddr(copyAddr->getLoc(), copyAddr->getSrc(), copyAddr->getDest(), IsTake, IsNotInitialization); markForDeletion(release); markForDeletion(copyAddr); markForDeletion(LI); return true; } // %5 = load %4 : $*LargeThing // release_value %5 : $LargeThing // copy_addr [take] %0 to [initialization] %4 : $*LargeThing // -> // copy_addr [take] %0 to %4 if (auto *release = dyn_cast(&*nextIt)) { if (release->getOperand() != LI || ignore(release)) return false; if (!LI->hasOneUse()) return false; auto next2It = std::next(nextIt); if (next2It == BB.end()) return false; auto *copyAddr = dyn_cast(&*next2It); if (!copyAddr || copyAddr->getDest() != LI->getOperand() || !copyAddr->isTakeOfSrc() || !copyAddr->isInitializationOfDest() || ignore(copyAddr)) return false; SILBuilderWithScope builder(LI); builder.createCopyAddr(copyAddr->getLoc(), copyAddr->getSrc(), copyAddr->getDest(), IsTake, IsNotInitialization); markForDeletion(release); markForDeletion(copyAddr); markForDeletion(LI); return true; } return false; } namespace { struct Properties { private: bool sawConstructor = false; bool sawProjection = false; bool sawFunctionUseOrReturn = false; bool sawLoad = false; bool sawStore = false; uint16_t use = 0; uint16_t numRegisters = 0; public: static uint16_t MaxNumUses; static uint16_t MaxNumRegisters; void addConstructor() { sawConstructor = true; } bool hasConstructorUse() const { return sawConstructor; } void addProjection() { sawProjection = true; } bool hasProjection() const { return sawProjection; } void addFunctionUseOrReturn() { sawFunctionUseOrReturn = true; } bool hasFunctionUseOrReturn() const { return sawFunctionUseOrReturn; } void addLoad() { sawLoad = true; } bool hasLoad() const { return sawLoad; } void addStore() { sawStore = true; } bool hasStore() const { return sawStore; } void addUse() { if (use == MaxNumUses) return; ++use; } uint16_t numUses() const { return use; } void setAsVeryLargeType() { setNumRegisters(MaxNumRegisters); } void setNumRegisters(unsigned regs) { if (regs > MaxNumRegisters) { numRegisters = MaxNumRegisters; return; } numRegisters = regs; } uint16_t getNumRegisters() const { return numRegisters; } }; } uint16_t Properties::MaxNumUses = 65535; uint16_t Properties::MaxNumRegisters = 65535; namespace { class LargeLoadableHeuristic { GenericEnvironment *genEnv; IRGenModule *irgenModule; llvm::DenseMap largeTypeProperties; static const unsigned NumRegistersVeryLargeType = 16; static const unsigned NumRegistersLargeType = 8; static const unsigned numUsesThreshold = 8; bool UseAggressiveHeuristic; public: LargeLoadableHeuristic(IRGenModule *irgenModule, GenericEnvironment *genEnv, bool UseAggressiveHeuristic) : genEnv(genEnv), irgenModule(irgenModule), UseAggressiveHeuristic(UseAggressiveHeuristic) {} void visit(SILInstruction *i); void visit(SILArgument *arg); bool isLargeLoadableType(SILType ty); bool isPotentiallyCArray(SILType ty); void propagate(PostOrderFunctionInfo &po); private: bool isLargeLoadableTypeOld(SILType ty); unsigned numRegisters(SILType ty) { if (ty.isAddress() || ty.isClassOrClassMetatype()) { return 0; } auto canType = ty.getASTType(); if (canType->hasTypeParameter()) { assert(genEnv && "Expected a GenericEnv"); canType = genEnv->mapTypeIntoContext(canType)->getCanonicalType(); } if (canType.getAnyGeneric() || isa(canType) || ty.is()) { assert(ty.isObject() && "Expected only two categories: address and object"); assert(!canType->hasTypeParameter()); auto entry = largeTypeProperties[ty]; auto cached = entry.getNumRegisters(); if (cached) return cached; const TypeInfo &TI = irgenModule->getTypeInfoForLowered(canType); auto &nativeSchemaOrigParam = TI.nativeParameterValueSchema(*irgenModule); // Passed compactly in registers but kept in many explosions. // An example of this is a C struct with a char buffer e.g // `struct {char buf[28]; }`. auto explosionSchema = TI.getSchema(); auto res = std::max(nativeSchemaOrigParam.size(), explosionSchema.size()); entry.setNumRegisters(res); largeTypeProperties[ty] = entry; return entry.getNumRegisters(); } return 0; } }; } void LargeLoadableHeuristic::propagate(PostOrderFunctionInfo &po) { if (!UseAggressiveHeuristic) return; for (auto *BB : po.getPostOrder()) { for (auto &I : llvm::reverse(*BB)) { switch (I.getKind()) { case SILInstructionKind::UncheckedBitwiseCastInst: case SILInstructionKind::TupleExtractInst: case SILInstructionKind::StructExtractInst: { auto &proj = cast(I); if (isLargeLoadableType(proj.getType())) { auto opdTy = proj.getOperand(0)->getType(); auto entry = largeTypeProperties[opdTy]; entry.setAsVeryLargeType(); largeTypeProperties[opdTy] = entry; } } break; default: continue; } } } } void LargeLoadableHeuristic::visit(SILArgument *arg) { auto objType = arg->getType().getObjectType(); if (numRegisters(objType) < NumRegistersLargeType) return; auto entry = largeTypeProperties[objType]; for (auto *use : arg->getUses()) { auto *usr = use->getUser(); switch (usr->getKind()) { case SILInstructionKind::TupleExtractInst: case SILInstructionKind::StructExtractInst: { auto projectionTy = cast(usr)->getType(); if (numRegisters(projectionTy) >= NumRegistersLargeType) { entry.addProjection(); largeTypeProperties[objType] = entry; } break; } default: continue; } } } void LargeLoadableHeuristic::visit(SILInstruction *i) { if (!UseAggressiveHeuristic) return; // Heuristic to make sure that UncheckedBitCast on C unions don't cause // trouble (the type of a union has a single llvm register representing the // bitwidth). if (auto *bitcast = dyn_cast(i)) { auto opdTy = bitcast->getOperand()->getType(); auto numRegs = numRegisters(opdTy); if (numRegs < NumRegistersLargeType) { auto resTy = bitcast->getType(); if (numRegisters(resTy) > NumRegistersLargeType) { // Force the source type to be indirect. auto entry = largeTypeProperties[opdTy]; entry.setAsVeryLargeType(); largeTypeProperties[opdTy] = entry; return; } } } for (const auto &opd : i->getAllOperands()) { auto opdTy = opd.get()->getType(); auto objType = opdTy.getObjectType(); auto registerCount = numRegisters(objType); if (registerCount < NumRegistersLargeType) continue; auto entry = largeTypeProperties[objType]; switch (i->getKind()) { case SILInstructionKind::TupleExtractInst: case SILInstructionKind::StructExtractInst: { auto projectionTy = cast(i)->getType(); if (numRegisters(projectionTy) >= NumRegistersLargeType) entry.addProjection(); break; } case SILInstructionKind::EnumInst: case SILInstructionKind::TupleInst: case SILInstructionKind::StructInst: { entry.addConstructor(); auto userType = cast(i)->getType(); auto &userEntry = largeTypeProperties[userType]; userEntry.addConstructor(); break; } case SILInstructionKind::SwitchEnumInst: { for (auto &succ : i->getParent()->getSuccessors()) { auto *caseBB = succ.getBB(); if (caseBB->getArguments().size() == 0) continue; assert(caseBB->getArguments().size() == 1); auto caseTy = caseBB->getArgument(0)->getType(); if (numRegisters(caseTy) >= NumRegistersLargeType) entry.addProjection(); } break; } case SILInstructionKind::SelectEnumInst: break; case SILInstructionKind::LoadInst: entry.addLoad(); break; case SILInstructionKind::StoreInst: entry.addStore(); break; case SILInstructionKind::ReturnInst: case SILInstructionKind::ApplyInst: case SILInstructionKind::PartialApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::BeginApplyInst: entry.addFunctionUseOrReturn(); break; default: entry.addUse(); break; } largeTypeProperties[objType] = entry; } } bool LargeLoadableHeuristic::isPotentiallyCArray(SILType ty) { if (ty.isAddress() || ty.isClassOrClassMetatype()) { return false; } auto canType = ty.getASTType(); if (canType->hasTypeParameter()) { assert(genEnv && "Expected a GenericEnv"); canType = genEnv->mapTypeIntoContext(canType)->getCanonicalType(); } if (canType.getAnyGeneric() || isa(canType)) { assert(ty.isObject() && "Expected only two categories: address and object"); assert(!canType->hasTypeParameter()); const TypeInfo &TI = irgenModule->getTypeInfoForLowered(canType); auto explosionSchema = TI.getSchema(); if (explosionSchema.size() > 15) return true; } return false; } // The old heuristic to determine whether we deem a type as large. bool LargeLoadableHeuristic::isLargeLoadableTypeOld(SILType ty) { if (ty.isAddress() || ty.isClassOrClassMetatype()) { return false; } auto canType = ty.getASTType(); if (canType->hasTypeParameter()) { assert(genEnv && "Expected a GenericEnv"); canType = genEnv->mapTypeIntoContext(canType)->getCanonicalType(); } if (canType.getAnyGeneric() || isa(canType)) { assert(ty.isObject() && "Expected only two categories: address and object"); assert(!canType->hasTypeParameter()); const TypeInfo &TI = irgenModule->getTypeInfoForLowered(canType); auto &nativeSchemaOrigParam = TI.nativeParameterValueSchema(*irgenModule); if (nativeSchemaOrigParam.size() > 15) return true; auto explosionSchema = TI.getSchema(); if (explosionSchema.size() > 15) return true; } return false; } bool LargeLoadableHeuristic::isLargeLoadableType(SILType ty) { if (!UseAggressiveHeuristic) return isLargeLoadableTypeOld(ty); auto regs = numRegisters(ty); if (regs < NumRegistersLargeType) return false; auto it = largeTypeProperties.find(ty); if (it == largeTypeProperties.end()) { return regs >= NumRegistersVeryLargeType; } const auto entry = it->second; if (regs >= NumRegistersVeryLargeType) return true; if (entry.numUses() > numUsesThreshold) return true; if (entry.hasStore()) return true; if (entry.hasConstructorUse()) return true; if (entry.hasProjection()) return true; if (entry.hasLoad()) { return entry.hasConstructorUse() || entry.hasFunctionUseOrReturn(); } return false; } namespace { class AddressAssignment { // Map from a loaded SIL SSA value to and address value. llvm::DenseMap valueToAddressMap; SmallVector allocStacks; SmallVector toDelete; SmallVector, 16> toDeleteBlockArg; LargeLoadableHeuristic &heuristic; IRGenModule *irgenModule; SILFunction &currFn; public: AddressAssignment(LargeLoadableHeuristic &heuristic, IRGenModule *irgenModule, SILFunction &currFn) : heuristic(heuristic), irgenModule(irgenModule), currFn(currFn) {} void assign(SILInstruction *inst); AllocStackInst *createAllocStack(SILType ty); SILLocation getAutoLoc() { return RegularLocation::getAutoGeneratedLocation(); } SILBuilder getBuilder(SILBasicBlock::iterator it) { SILInstruction *inst = &(*it); SILBuilder builder(inst->getParent(), it); builder.setCurrentDebugScope(inst->getDebugScope()); return builder; } SILBuilder getTermBuilder(SILInstruction *term) { SILBuilder builder(term->getParent(), term->getParent()->end()); builder.setCurrentDebugScope(term->getDebugScope()); return builder; } const BuiltinInfo &getBuiltinInfo(Identifier id) { return irgenModule->getSILModule().getBuiltinInfo(id); } void markForDeletion(SILInstruction *inst) { toDelete.push_back(inst); } void markBlockArgumentForDeletion(SILBasicBlock *b, unsigned argIdx = 0) { toDeleteBlockArg.push_back(std::make_pair(b, argIdx)); } bool isPotentiallyCArray(SILType ty) { return heuristic.isPotentiallyCArray(ty); } bool isLargeLoadableType(SILType ty) { return heuristic.isLargeLoadableType(ty); } bool contains(SILValue v) { return valueToAddressMap.find(v) != valueToAddressMap.end(); } SILValue getAddressForValue(SILValue v) { auto it = valueToAddressMap.find(v); // This can happen if we deem a container type small but a contained type // big or we have an undef operand. if (it == valueToAddressMap.end()) { if (auto *sv = dyn_cast(v)) { auto addr = createAllocStack(v->getType()); auto builder = getBuilder(++sv->getIterator()); builder.createStore(sv->getLoc(), v, addr, StoreOwnershipQualifier::Unqualified); mapValueToAddress(v, addr); return addr; } if (isa(v)) { auto undefAddr = createAllocStack(v->getType()); mapValueToAddress(v, undefAddr); return undefAddr; } } assert(it != valueToAddressMap.end()); return it->second; } void mapValueToAddress(SILValue val, SILValue addr) { assert(!valueToAddressMap.count(val)); valueToAddressMap[val] = addr; } void finish(DominanceInfo *dominance, DeadEndBlocks *deadEnds); }; } // namespace void AddressAssignment::finish(DominanceInfo *dominance, DeadEndBlocks *deadEnds) { // Narrow the lifetime of alloc_stack instructions. for (auto *stackLoc : allocStacks) { auto insertPt = dominance->getLeastCommonAncestorOfUses(stackLoc)->begin(); stackLoc->moveBefore(&*insertPt); SmallVector boundary; computeDominatedBoundaryBlocks(stackLoc->getParent(), dominance, boundary); for (auto *block : boundary) { if (deadEnds->isDeadEnd(block)) continue; auto builder = getBuilder(block->back().getIterator()); builder.createDeallocStack(getAutoLoc(), stackLoc); } } for (auto *inst : llvm::reverse(toDelete)) { #ifndef NDEBUG for (auto res : inst->getResults()) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunreachable-code-loop-increment" for (auto *use : res->getUses()) { inst->dump(); use->getUser()->dump(); inst->getFunction()->dump(); break; } #pragma clang diagnostic pop } #endif inst->eraseFromParent(); } for (auto bbIdx : reverse(toDeleteBlockArg)) { bbIdx.first->eraseArgument(bbIdx.second); } StackNesting::fixNesting(&currFn); } namespace { class AssignAddressToDef : SILInstructionVisitor { friend SILVisitorBase; friend SILInstructionVisitor; AddressAssignment &assignment; SILValue origValue; public: AssignAddressToDef(AddressAssignment &assignment, SILValue orig) : assignment(assignment), origValue(orig) {} void rewriteValue(); protected: void singleValueInstructionFallback(SingleValueInstruction *inst) { // Map all the large arguments back to values. for (auto &opd : inst->getAllOperands()) { if (assignment.isLargeLoadableType(opd.get()->getType())) { auto builder = assignment.getBuilder(inst->getIterator()); auto addr = assignment.getAddressForValue(opd.get()); auto loaded = builder.createLoad(inst->getLoc(), addr, LoadOwnershipQualifier::Unqualified); opd.set(loaded); } } // Store the result back to a stack location. if (assignment.isLargeLoadableType(inst->getType())) { auto builder = assignment.getBuilder(++inst->getIterator()); auto addr = assignment.createAllocStack(inst->getType()); assignment.mapValueToAddress(origValue, addr); builder.createStore(inst->getLoc(), origValue, addr, StoreOwnershipQualifier::Unqualified); } } void visitSILInstruction(SILInstruction *inst) { #ifdef NDEBUG if (auto *sv = dyn_cast(inst)) { singleValueInstructionFallback(sv); return; } #endif #ifndef NDEBUG inst->dump(); inst->getFunction()->dump(); #endif llvm::report_fatal_error("Unimplemented definition"); } void visitKeyPathInst(KeyPathInst *kp) { singleValueInstructionFallback(kp); } void visitMarkDependenceInst(MarkDependenceInst *mark) { // This instruction is purely for semantic tracking in SIL. // Simply forward the value and delete the instruction. auto valAddr = assignment.getAddressForValue(mark->getValue()); assignment.mapValueToAddress(mark, valAddr); assignment.markForDeletion(mark); } void visitBeginApplyInst(BeginApplyInst *apply) { auto builder = assignment.getBuilder(++apply->getIterator()); auto addr = assignment.createAllocStack(origValue->getType()); assignment.mapValueToAddress(origValue, addr); for (auto &opd : apply->getAllOperands()) { if (assignment.contains(opd.get())) { auto builder = assignment.getBuilder(apply->getIterator()); auto loaded = builder.createLoad( apply->getLoc(), assignment.getAddressForValue(opd.get()), LoadOwnershipQualifier::Unqualified); opd.set(loaded); } } builder.createStore(apply->getLoc(), origValue, addr, StoreOwnershipQualifier::Unqualified); } void visitApplyInst(ApplyInst *apply) { // The loadable by address transformation ignores large tuple return types. auto builder = assignment.getBuilder(++apply->getIterator()); auto addr = assignment.createAllocStack(apply->getType()); assignment.mapValueToAddress(origValue, addr); for (auto &opd : apply->getAllOperands()) { if (assignment.contains(opd.get())) { auto builder = assignment.getBuilder(apply->getIterator()); auto loaded = builder.createLoad( apply->getLoc(), assignment.getAddressForValue(opd.get()), LoadOwnershipQualifier::Unqualified); opd.set(loaded); } } builder.createStore(apply->getLoc(), origValue, addr, StoreOwnershipQualifier::Unqualified); } void visitLoadInst(LoadInst *load) { auto builder = assignment.getBuilder(load->getIterator()); auto addr = assignment.createAllocStack(load->getType()); assignment.mapValueToAddress(origValue, addr); builder.createCopyAddr(load->getLoc(), load->getOperand(), addr, IsTake, IsInitialization); swift::salvageLoadDebugInfo(load); assignment.markForDeletion(load); } void visitEnumInst(EnumInst *e) { auto builder = assignment.getBuilder(e->getIterator()); auto enumAddr = assignment.createAllocStack(e->getType()); assignment.mapValueToAddress(origValue, enumAddr); if (e->hasOperand()) { auto opd = e->getOperand(); auto initAddr = builder.createInitEnumDataAddr( assignment.getAutoLoc(), enumAddr, e->getElement(), opd->getType().getAddressType()); if (assignment.contains(opd)) { SILValue opdAddr = assignment.getAddressForValue(opd); builder.createCopyAddr(e->getLoc(), opdAddr, initAddr, IsTake, IsInitialization); } else { builder.createStore(e->getLoc(), opd, initAddr, StoreOwnershipQualifier::Unqualified); } } builder.createInjectEnumAddr(e->getLoc(), enumAddr, e->getElement()); assignment.markForDeletion(e); } void visitStructInst(StructInst *s) { auto builder = assignment.getBuilder(s->getIterator()); auto structAddr = assignment.createAllocStack(s->getType()); assignment.mapValueToAddress(origValue, structAddr); auto fieldIt = s->getStructDecl()->getStoredProperties().begin(); for (auto &opd : s->getAllOperands()) { auto projAddr = builder.createStructElementAddr( s->getLoc(), structAddr, *fieldIt, opd.get()->getType().getAddressType()); fieldIt++; if (assignment.contains(opd.get())) { auto opdAddr = assignment.getAddressForValue(opd.get()); builder.createCopyAddr(s->getLoc(), opdAddr, projAddr, IsTake, IsInitialization); } else { builder.createStore(s->getLoc(), opd.get(), projAddr, StoreOwnershipQualifier::Unqualified); } } assignment.markForDeletion(s); } void visitTupleInst(TupleInst *t) { auto builder = assignment.getBuilder(t->getIterator()); auto tupleAddr = assignment.createAllocStack(t->getType()); assignment.mapValueToAddress(origValue, tupleAddr); for (auto &opd : t->getAllOperands()) { auto projAddr = builder.createTupleElementAddr( t->getLoc(), tupleAddr, opd.getOperandNumber(), opd.get()->getType().getAddressType()); if (assignment.contains(opd.get())) { auto opdAddr = assignment.getAddressForValue(opd.get()); builder.createCopyAddr(t->getLoc(), opdAddr, projAddr, IsTake, IsInitialization); } else { builder.createStore(t->getLoc(), opd.get(), projAddr, StoreOwnershipQualifier::Unqualified); } } assignment.markForDeletion(t); } void visitTupleExtractInst(TupleExtractInst *extract) { auto builder = assignment.getBuilder(extract->getIterator()); auto opdAddr = assignment.getAddressForValue(extract->getOperand()); auto projAddr = builder.createTupleElementAddr( extract->getLoc(), opdAddr, extract->getFieldIndex(), extract->getType().getAddressType()); assignment.mapValueToAddress(origValue, projAddr); assignment.markForDeletion(extract); } void visitStructExtractInst(StructExtractInst *extract) { auto builder = assignment.getBuilder(extract->getIterator()); // Handle undef operands. if (isa(extract->getOperand())) { auto extractAddr = assignment.createAllocStack(extract->getType()); assignment.mapValueToAddress(origValue, extractAddr); assignment.markForDeletion(extract); return; } auto opdAddr = assignment.getAddressForValue(extract->getOperand()); auto projAddr = builder.createStructElementAddr( extract->getLoc(), opdAddr, extract->getField(), extract->getType().getAddressType()); assignment.mapValueToAddress(origValue, projAddr); assignment.markForDeletion(extract); } void visitUncheckedEnumDataInst(UncheckedEnumDataInst *enumData) { auto builder = assignment.getBuilder(enumData->getIterator()); auto opd = enumData->getOperand(); SILValue opdAddr; if (assignment.contains(opd)) { opdAddr = assignment.getAddressForValue(enumData->getOperand()); // builder.createUncheckedTakeEnumDataAddr is destructive. // So we need to copy to keep the original address location valid. auto addr = assignment.createAllocStack(opd->getType()); builder.createCopyAddr(enumData->getLoc(), opdAddr, addr, IsTake, IsInitialization); opdAddr = addr; // TODO: we could omit the copy if this is the only user of the operand. } else { opdAddr = assignment.createAllocStack(opd->getType()); builder.createStore(enumData->getLoc(), opd, opdAddr, StoreOwnershipQualifier::Unqualified); } auto extractAddr = builder.createUncheckedTakeEnumDataAddr( enumData->getLoc(), opdAddr, enumData->getElement(), enumData->getType().getAddressType()); assignment.mapValueToAddress(origValue, extractAddr); assignment.markForDeletion(enumData); } void visitBuiltinInst(BuiltinInst *bi) { if (assignment.getBuiltinInfo(bi->getName()).ID == BuiltinValueKind::ZeroInitializer) { auto build = assignment.getBuilder(++bi->getIterator()); auto newAddr = assignment.createAllocStack(bi->getType()); build.createZeroInitAddr(bi->getLoc(), newAddr); assignment.mapValueToAddress(origValue, newAddr); assignment.markForDeletion(bi); } else { singleValueInstructionFallback(bi); } } void visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *bc) { auto builder = assignment.getBuilder(bc->getIterator()); if (assignment.isLargeLoadableType(bc->getType()) && !assignment.isLargeLoadableType(bc->getOperand()->getType())) { // Curious case of an imported C union. // The union is imported as a struct that has no fields. // When we access union members we instead bitcast to the union member // type. // We expect the "in" type to be larger or equal in size to the "out" // type. See IRGenSILFunction::visitUncheckedTrivialBitCastInst. // We therefore must use the bigger type, i.e the operand type, to create // a stack allocation. auto opdAddr = assignment.createAllocStack(bc->getOperand()->getType()); // Try load -> store forwarding. auto peephole = dyn_cast(bc->getOperand()); if (peephole && peephole->getParent() == bc->getParent() && (++peephole->getIterator()) == bc->getIterator()) { builder.createCopyAddr(bc->getLoc(), peephole->getOperand(), opdAddr, IsTake, IsInitialization); } else { builder.createStore(bc->getLoc(), bc->getOperand(), opdAddr, StoreOwnershipQualifier::Unqualified); } auto addr = builder.createUncheckedAddrCast( bc->getLoc(), opdAddr, bc->getType().getAddressType()); assignment.mapValueToAddress(origValue, addr); assignment.markForDeletion(bc); return; } auto opdAddr = assignment.getAddressForValue(bc->getOperand()); auto newAddr = builder.createUncheckedAddrCast( bc->getLoc(), opdAddr, bc->getType().getAddressType()); assignment.mapValueToAddress(origValue, newAddr); assignment.markForDeletion(bc); } void visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *bc) { auto builder = assignment.getBuilder(bc->getIterator()); auto opdAddr = assignment.getAddressForValue(bc->getOperand()); auto newAddr = builder.createUncheckedAddrCast( bc->getLoc(), opdAddr, bc->getType().getAddressType()); assignment.mapValueToAddress(origValue, newAddr); assignment.markForDeletion(bc); } }; } // namespace void AssignAddressToDef::rewriteValue() { auto *inst = origValue.getDefiningInstruction(); visit(inst); } namespace { class RewriteUser : SILInstructionVisitor { friend SILVisitorBase; friend SILInstructionVisitor; AddressAssignment &assignment; SILInstruction *user; public: RewriteUser(AddressAssignment &assignment, SILInstruction *user) : assignment(assignment), user(user) {} void rewrite() { visit(user); } protected: void userInstructionFallback(SILInstruction *inst) { // Map all the large arguments back to values. for (auto &opd : inst->getAllOperands()) { if (assignment.isLargeLoadableType(opd.get()->getType())) { auto builder = assignment.getBuilder(inst->getIterator()); auto addr = assignment.getAddressForValue(opd.get()); auto loaded = builder.createLoad(inst->getLoc(), addr, LoadOwnershipQualifier::Unqualified); opd.set(loaded); } } } void visitSILInstruction(SILInstruction *inst) { #ifdef NDEBUG userInstructionFallback(inst); return; #endif #ifndef NDEBUG inst->dump(); inst->getFunction()->dump(); llvm::report_fatal_error("Unimplemented user"); #endif } void visitKeyPathInst(KeyPathInst *kp) { userInstructionFallback(kp); } void visitYieldInst(YieldInst *yield) { userInstructionFallback(yield); } void visitThrowInst(ThrowInst *t) { userInstructionFallback(t); } void visitCheckedCastBranchInst(CheckedCastBranchInst *c) { userInstructionFallback(c); } void visitFixLifetimeInst(FixLifetimeInst *f) { auto addr = assignment.getAddressForValue(f->getOperand()); auto builder = assignment.getBuilder(f->getIterator()); builder.createFixLifetime(f->getLoc(), addr); assignment.markForDeletion(f); } void visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *bc) { auto addr = assignment.getAddressForValue(bc->getOperand()); auto builder = assignment.getBuilder(bc->getIterator()); auto loaded = builder.createLoad(bc->getLoc(), addr, LoadOwnershipQualifier::Unqualified); auto newVal = builder.createUncheckedTrivialBitCast(bc->getLoc(), loaded, bc->getType()); bc->replaceAllUsesWith(newVal); assignment.markForDeletion(bc); } void visitEnumInst(EnumInst *e) { assert(!assignment.isLargeLoadableType(e->getType())); auto opd = e->getOperand(); auto opdAddr = assignment.getAddressForValue(opd); auto builder = assignment.getBuilder(e->getIterator()); auto enumAddr = assignment.createAllocStack(e->getType()); auto initAddr = builder.createInitEnumDataAddr( assignment.getAutoLoc(), enumAddr, e->getElement(), opd->getType().getAddressType()); builder.createCopyAddr(e->getLoc(), opdAddr, initAddr, IsTake, IsInitialization); builder.createInjectEnumAddr(e->getLoc(), enumAddr, e->getElement()); auto enumVal = builder.createLoad(e->getLoc(), enumAddr, LoadOwnershipQualifier::Unqualified); e->replaceAllUsesWith(enumVal); assignment.markForDeletion(e); } void visitApplySite(ApplySite apply) { for (auto &opd : apply.getArgumentOperands()) { if (assignment.contains(opd.get())) { auto builder = assignment.getBuilder(apply->getIterator()); auto loaded = builder.createLoad( apply->getLoc(), assignment.getAddressForValue(opd.get()), LoadOwnershipQualifier::Unqualified); opd.set(loaded); } } } void visitApplyInst(ApplyInst *apply) { visitApplySite(apply); } void visitBeginApplyInst(BeginApplyInst *apply) { visitApplySite(apply); } void visitTryApplyInst(TryApplyInst *apply) { visitApplySite(apply); } void visitPartialApplyInst(PartialApplyInst *apply) { visitApplySite(apply); } void visitReturnInst(ReturnInst *r) { auto builder = assignment.getBuilder(r->getIterator()); auto opdAddr = assignment.getAddressForValue(r->getOperand()); auto newVal = builder.createLoad(assignment.getAutoLoc(), opdAddr, LoadOwnershipQualifier::Unqualified); auto termBuilder = assignment.getTermBuilder(r); termBuilder.createReturn(r->getLoc(), newVal); assignment.markForDeletion(r); } void visitMarkDependenceInst(MarkDependenceInst *m) { auto builder = assignment.getBuilder(m->getIterator()); auto opdAddr = assignment.getAddressForValue(m->getBase()); auto newValue = builder.createMarkDependence(m->getLoc(), m->getValue(), opdAddr, m->dependenceKind()); m->replaceAllUsesWith(newValue); assignment.markForDeletion(m); } void visitStoreInst(StoreInst *store) { auto builder = assignment.getBuilder(store->getIterator()); SILValue addr = assignment.getAddressForValue(store->getSrc()); builder.createCopyAddr(store->getLoc(), addr, store->getDest(), IsTake, IsInitialization); assignment.markForDeletion(store); } bool overlapsWithOnStackDebugLoc(SILValue v) { for (auto *use : v->getUses()) { auto *store = dyn_cast(use->getUser()); if (!store) continue; auto *stackLoc = dyn_cast(store->getDest()); if (!stackLoc) continue; if (getAnyDebugUse(stackLoc)) return true; } return false; } void visitDebugValueInst(DebugValueInst *dbg) { if (!dbg->hasAddrVal() && (assignment.isPotentiallyCArray(dbg->getOperand()->getType()) || overlapsWithOnStackDebugLoc(dbg->getOperand()))) { assignment.markForDeletion(dbg); return; } auto builder = assignment.getBuilder(dbg->getIterator()); SILValue addr = assignment.getAddressForValue(dbg->getOperand()); builder.createDebugValueAddr(dbg->getLoc(), addr, *dbg->getVarInfo()); assignment.markForDeletion(dbg); } void visitRetainValueInst(RetainValueInst *r) { auto builder = assignment.getBuilder(r->getIterator()); SILValue addr = assignment.getAddressForValue(r->getOperand()); builder.createRetainValueAddr(r->getLoc(), addr, r->getAtomicity()); assignment.markForDeletion(r); } void visitReleaseValueInst(ReleaseValueInst *r) { auto builder = assignment.getBuilder(r->getIterator()); SILValue addr = assignment.getAddressForValue(r->getOperand()); builder.createReleaseValueAddr(r->getLoc(), addr, r->getAtomicity()); assignment.markForDeletion(r); } void visitSelectEnumInst(SelectEnumInst *select) { auto builder = assignment.getBuilder(select->getIterator()); SmallVector> caseValues; for (unsigned idx = 0, cnt = select->getNumCases(); idx < cnt; ++idx) { caseValues.push_back(select->getCase(idx)); } SILValue opAddr = assignment.getAddressForValue(select->getOperand()); SILValue defaultValue; if (select->hasDefault()) defaultValue = select->getDefaultResult(); auto res = builder.createSelectEnumAddr( select->getLoc(), opAddr, select->getType(), defaultValue, caseValues); select->replaceAllUsesWith(res); assignment.markForDeletion(select); } void visitStructExtractInst(StructExtractInst *extract) { if (isa(extract->getOperand())) return; auto builder = assignment.getBuilder(extract->getIterator()); auto opdAddr = assignment.getAddressForValue(extract->getOperand()); auto projAddr = builder.createStructElementAddr( extract->getLoc(), opdAddr, extract->getField(), extract->getType().getAddressType()); auto load = builder.createLoad(extract->getLoc(), projAddr, LoadOwnershipQualifier::Unqualified); extract->replaceAllUsesWith(load); assignment.markForDeletion(extract); } void visitTupleExtractInst(TupleExtractInst *extract) { auto builder = assignment.getBuilder(extract->getIterator()); auto opdAddr = assignment.getAddressForValue(extract->getOperand()); auto projAddr = builder.createTupleElementAddr( extract->getLoc(), opdAddr, extract->getFieldIndex(), extract->getType().getAddressType()); auto load = builder.createLoad(extract->getLoc(), projAddr, LoadOwnershipQualifier::Unqualified); extract->replaceAllUsesWith(load); assignment.markForDeletion(extract); } void visitSwitchEnumInst(SwitchEnumInst *sw) { auto opdAddr = assignment.getAddressForValue(sw->getOperand()); { auto addr = assignment.createAllocStack(sw->getOperand()->getType()); // UncheckedTakeEnumDataAddr is destructive. // So we need to copy to keep the original address location valid. auto builder = assignment.getBuilder(sw->getIterator()); builder.createCopyAddr(sw->getLoc(), opdAddr, addr, IsTake, IsInitialization); opdAddr = addr; } auto loc = sw->getLoc(); auto rewriteCase = [&](EnumElementDecl *caseDecl, SILBasicBlock *caseBB) { // Nothing to do for unused case payloads. if (caseBB->getArguments().size() == 0) return; assert(caseBB->getArguments().size() == 1); SILArgument *caseArg = caseBB->getArgument(0); assert(caseDecl->hasAssociatedValues() && "caseBB has a payload argument"); SILBuilder caseBuilder = assignment.getBuilder(caseBB->begin()); auto *caseAddr = caseBuilder.createUncheckedTakeEnumDataAddr(loc, opdAddr, caseDecl, caseArg->getType().getAddressType()); if (assignment.isLargeLoadableType(caseArg->getType())) { assignment.mapValueToAddress(caseArg, caseAddr); assignment.markBlockArgumentForDeletion(caseBB); } else { auto *caseLoad = caseBuilder.createLoad( loc, caseAddr, LoadOwnershipQualifier::Unqualified); caseArg->replaceAllUsesWith(caseLoad); caseBB->eraseArgument(0); } }; SmallVector, 8> cases; SmallVector caseCounters; // Collect switch cases for rewriting and remove block arguments. for (unsigned caseIdx : range(sw->getNumCases())) { auto caseDeclAndBB = sw->getCase(caseIdx); EnumElementDecl *caseDecl = caseDeclAndBB.first; SILBasicBlock *caseBB = caseDeclAndBB.second; cases.push_back(caseDeclAndBB); caseCounters.push_back(sw->getCaseCount(caseIdx)); rewriteCase(caseDecl, caseBB); } SILBasicBlock *defaultBB = nullptr; auto defaultCounter = ProfileCounter(); if (sw->hasDefault()) { defaultBB = sw->getDefaultBB(); defaultCounter = sw->getDefaultCount(); if (auto defaultDecl = sw->getUniqueCaseForDefault()) { rewriteCase(defaultDecl.get(), defaultBB); } else if (defaultBB->getNumArguments() == 0) { // nothing to do there. } else { SILArgument *arg = defaultBB->getArgument(0); #ifndef NDEBUG arg->dump(); sw->dump(); #endif llvm::report_fatal_error("Unhandle switch_enum"); } } auto builder = assignment.getTermBuilder(sw); builder.createSwitchEnumAddr(loc, opdAddr, defaultBB, cases, ArrayRef(caseCounters), defaultCounter); sw->eraseFromParent(); } void visitUncheckedEnumDataInst(UncheckedEnumDataInst *enumData) { auto builder = assignment.getBuilder(enumData->getIterator()); auto opdAddr = assignment.getAddressForValue(enumData->getOperand()); auto loc = enumData->getLoc(); // UncheckedTakeEnumDataAddrInst is destructive. auto addr = assignment.createAllocStack(enumData->getOperand()->getType()); builder.createCopyAddr(enumData->getLoc(), opdAddr, addr, IsTake, IsInitialization); // TODO: we could omit the copy if this is the only user of the operand. auto extractAddr = builder.createUncheckedTakeEnumDataAddr( loc, addr, enumData->getElement(), enumData->getType().getAddressType()); auto newVal = builder.createLoad(loc, extractAddr, LoadOwnershipQualifier::Unqualified); enumData->replaceAllUsesWith(newVal); assignment.markForDeletion(enumData); } void visitBranchInst(BranchInst *br) { SmallVector newArgs; auto *succBB = br->getParent()->getSingleSuccessorBlock(); unsigned idx = 0; // We need to perform a parallel copy of the phi arguments (because of // cycles) // First collect the sources of the copy. llvm::DenseSet oldSources; for (auto arg : br->getArgs()) { auto ty = arg->getType(); if (!assignment.isLargeLoadableType(ty)) { idx++; continue; } auto addr = assignment.getAddressForValue(arg); oldSources.insert(addr); idx++; } // Remap (copy) any source that is also a destination to a new stack // location. llvm::DenseMap remappedSource; idx = 0; for (auto arg : br->getArgs()) { auto ty = arg->getType(); if (!assignment.isLargeLoadableType(ty)) { idx++; continue; } auto destAddr = assignment.getAddressForValue(succBB->getArgument(idx)); if (oldSources.count(destAddr)) { auto newSrc = assignment.createAllocStack(destAddr->getType()); auto builder = assignment.getBuilder(br->getIterator()); builder.createCopyAddr(assignment.getAutoLoc(), destAddr, newSrc, IsTake, IsInitialization); remappedSource[destAddr] = newSrc; } idx++; } idx = 0; for (auto arg : br->getArgs()) { auto ty = arg->getType(); if (!assignment.isLargeLoadableType(ty)) { newArgs.push_back(arg); idx++; continue; } auto addr = assignment.getAddressForValue(arg); // Use the remapped source location if necessary. if (remappedSource.find(addr) != remappedSource.end()) { addr = remappedSource[addr]; } auto builder = assignment.getBuilder(br->getIterator()); auto destAddr = assignment.getAddressForValue(succBB->getArgument(idx)); builder.createCopyAddr(assignment.getAutoLoc(), addr, destAddr, IsTake, IsInitialization); idx++; } auto builder = assignment.getTermBuilder(br); builder.createBranch(br->getLoc(), br->getDestBB(), newArgs); assignment.markForDeletion(br); } void visitValueMetatypeInst(ValueMetatypeInst *metatype) { auto builder = assignment.getBuilder(metatype->getIterator()); auto opdAddr = assignment.getAddressForValue(metatype->getOperand()); auto loc = metatype->getLoc(); auto loaded = builder.createLoad(loc, opdAddr, LoadOwnershipQualifier::Unqualified); auto newVal = builder.createValueMetatype(loc, metatype->getType(), loaded); metatype->replaceAllUsesWith(newVal); assignment.markForDeletion(metatype); } }; } // namespace AllocStackInst *AddressAssignment::createAllocStack(SILType ty) { auto insertPt = currFn.getEntryBlock()->front().getIterator(); auto builder = getBuilder(insertPt); auto as = builder.createAllocStack(getAutoLoc(), ty); allocStacks.push_back(as); return as; } void AddressAssignment::assign(SILInstruction *inst) { bool rewritten = false; // Rewrite definitions to addresses. for (auto val : inst->getResults()) { auto ty = val->getType(); if (!ty.isObject()) continue; if (!isLargeLoadableType(ty)) continue; AssignAddressToDef a(*this, val); a.rewriteValue(); rewritten = true; } if (rewritten) return; // Rewrite users to use addresses. for (auto &opd : inst->getAllOperands()) { SILValue val = opd.get(); auto ty = val->getType(); if (!ty.isObject()) continue; if (!isLargeLoadableType(ty)) continue; RewriteUser r(*this, inst); r.rewrite(); return; } } static void runPeepholesAndReg2Mem(SILPassManager *pm, SILModule *silMod, IRGenModule *irgenModule) { if (!irgenModule->getOptions().EnableLargeLoadableTypesReg2Mem) return; for (SILFunction &currF : *silMod) { if (!currF.isDefinition()) continue; GenericEnvironment *genEnv = getSubstGenericEnvironment(&currF); removeUnreachableBlocks(currF); // copy_addr peepholes bool UseAggressiveHeuristic = silMod->getOptions().UseAggressiveReg2MemForCodeSize; LargeLoadableHeuristic heuristic(irgenModule, genEnv, UseAggressiveHeuristic); Peepholes opts(pm, silMod, irgenModule); for (SILBasicBlock &BB : currF) { for (auto *arg : BB.getArguments()) { heuristic.visit(arg); } for (SILInstruction &I : BB) { heuristic.visit(&I); if (opts.ignore(&I)) continue; if (opts.optimizeLoad(BB, &I)) continue; } } // Delete replaced instructions. opts.deleteInstructions(); PostOrderFunctionInfo postorderInfo(&currF); heuristic.propagate(postorderInfo); AddressAssignment assignment(heuristic, irgenModule, currF); // Assign addresses to basic block arguments. // Store alloc_stack's we created that need to be initialized after we // proccess the original instructions in the function. SmallVector, 8> largeArguments; SmallVector, 8> largeTryApplyResults; auto *entryBB = currF.getEntryBlock(); for (auto &bb : currF) { // Assign addresses for large entry block arguments. if (&bb == entryBB) { for (auto *arg : bb.getArguments()) { auto ty = arg->getType(); if (assignment.isLargeLoadableType(ty)) { auto addr = assignment.createAllocStack(ty); assignment.mapValueToAddress(arg, addr); // We will emit the store to initialize after parsing all other // instructions so that we don't accidentally rewrite it to an // by-address insttruction. largeArguments.push_back(std::make_pair(addr, SILValue(arg))); } } continue; } // Switch enum successor blocks are handle when processing the // switch_enum. if (auto *pred = bb.getSinglePredecessorBlock()) { // switch_enum handles the basic block arguments independently. if (auto sw = dyn_cast(pred->getTerminator())) { if (assignment.isLargeLoadableType(sw->getOperand()->getType())) { continue; } } } // Handle all other basic block arguments. for (auto *arg : bb.getArguments()) { auto ty = arg->getType(); if (assignment.isLargeLoadableType(ty)) { auto addr = assignment.createAllocStack(ty); assignment.mapValueToAddress(arg, addr); bool shouldDeleteBlockArgument = true; // Large try apply results have to be stored to their stack location // in the success block. if (auto *pred = bb.getSinglePredecessorBlock()) { if (isa(pred->getTerminator()) || isa(pred->getTerminator()) || isa(pred->getTerminator())) { assert(bb.getArguments().size() == 1); shouldDeleteBlockArgument = false; // We will emit the store to initialize after parsing all other // instructions so that we don't accidentally rewrite it to an // by-address insttruction. largeTryApplyResults.push_back(std::make_pair(addr, arg)); } } if (shouldDeleteBlockArgument) assignment.markBlockArgumentForDeletion(&bb, arg->getIndex()); } } } // Asign addresses to non-address SSA values. for (auto *BB : postorderInfo.getReversePostOrder()) { SmallVector origInsts; for (SILInstruction &i : *BB) { origInsts.push_back(&i); } for (SILInstruction *i : origInsts) assignment.assign(i); } // Emit stores for large arguments to their address location. for (auto largeArgPair : largeArguments) { auto *addr = largeArgPair.first; auto arg = largeArgPair.second; auto builder = assignment.getBuilder(++addr->getIterator()); builder.createStore(assignment.getAutoLoc(), arg, addr, StoreOwnershipQualifier::Unqualified); } // Emit stores for large results to their address location. for (auto largeResultPair : largeTryApplyResults) { auto *addr = largeResultPair.first; auto *arg = largeResultPair.second; auto *bb = arg->getParent(); auto builder = assignment.getBuilder(bb->begin()); builder.createStore(assignment.getAutoLoc(), arg, addr, StoreOwnershipQualifier::Unqualified); } auto *dominance = pm->getAnalysis(); auto *deadEnds = pm->getAnalysis(); assignment.finish(dominance->get(&currF), deadEnds->get(&currF)); pm->invalidateAnalysis( &currF, SILAnalysis::InvalidationKind::BranchesAndInstructions); } } SILTransform *irgen::createLoadableByAddress() { return new LoadableByAddress(); }