//===--- Context.swift - defines the context types ------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2022 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 // //===----------------------------------------------------------------------===// import SIL import OptimizerBridging /// The base type of all contexts. protocol Context { var _bridged: BridgedPassContext { get } } extension Context { var options: Options { Options(_bridged: _bridged) } var diagnosticEngine: DiagnosticEngine { return DiagnosticEngine(bridged: _bridged.getDiagnosticEngine()) } // The calleeAnalysis is not specific to a function and therefore can be provided in // all contexts. var calleeAnalysis: CalleeAnalysis { let bridgeCA = _bridged.getCalleeAnalysis() return CalleeAnalysis(bridged: bridgeCA) } var hadError: Bool { _bridged.hadError() } var silStage: SILStage { switch _bridged.getSILStage() { case .Raw: return .raw case .Canonical: return .canonical case .Lowered: return .lowered default: fatalError("unhandled SILStage case") } } var moduleIsSerialized: Bool { _bridged.moduleIsSerialized() } func lookupDeinit(ofNominal: NominalTypeDecl) -> Function? { _bridged.lookUpNominalDeinitFunction(ofNominal.bridged).function } func getBuiltinIntegerType(bitWidth: Int) -> Type { _bridged.getBuiltinIntegerType(bitWidth).type } } /// A context which allows mutation of a function's SIL. protocol MutatingContext : Context { // Called by all instruction mutations, including inserted new instructions. var notifyInstructionChanged: (Instruction) -> () { get } } extension MutatingContext { func notifyInvalidatedStackNesting() { _bridged.notifyInvalidatedStackNesting() } var needFixStackNesting: Bool { _bridged.getNeedFixStackNesting() } /// Splits the basic block, which contains `inst`, before `inst` and returns the /// new block. /// /// `inst` and all subsequent instructions are moved to the new block, while all /// instructions _before_ `inst` remain in the original block. func splitBlock(before inst: Instruction) -> BasicBlock { notifyBranchesChanged() return _bridged.splitBlockBefore(inst.bridged).block } /// Splits the basic block, which contains `inst`, after `inst` and returns the /// new block. /// /// All subsequent instructions after `inst` are moved to the new block, while `inst` and all /// instructions _before_ `inst` remain in the original block. func splitBlock(after inst: Instruction) -> BasicBlock { notifyBranchesChanged() return _bridged.splitBlockAfter(inst.bridged).block } func createBlock(after block: BasicBlock) -> BasicBlock { notifyBranchesChanged() return _bridged.createBlockAfter(block.bridged).block } func erase(instruction: Instruction) { if instruction is FullApplySite { notifyCallsChanged() } if instruction is TermInst { notifyBranchesChanged() } notifyInstructionsChanged() _bridged.eraseInstruction(instruction.bridged) } func erase(instructionIncludingAllUsers inst: Instruction) { if inst.isDeleted { return } for result in inst.results { for use in result.uses { erase(instructionIncludingAllUsers: use.instruction) } } erase(instruction: inst) } func erase(instructionIncludingDebugUses inst: Instruction) { precondition(inst.results.allSatisfy { $0.uses.ignoreDebugUses.isEmpty }) erase(instructionIncludingAllUsers: inst) } func erase(block: BasicBlock) { _bridged.eraseBlock(block.bridged) } func tryOptimizeApplyOfPartialApply(closure: PartialApplyInst) -> Bool { if _bridged.tryOptimizeApplyOfPartialApply(closure.bridged) { notifyInstructionsChanged() notifyCallsChanged() for use in closure.callee.uses { if use.instruction is FullApplySite { notifyInstructionChanged(use.instruction) } } return true } return false } func tryDeleteDeadClosure(closure: SingleValueInstruction, needKeepArgsAlive: Bool = true) -> Bool { if _bridged.tryDeleteDeadClosure(closure.bridged, needKeepArgsAlive) { notifyInstructionsChanged() return true } return false } func tryDevirtualize(apply: FullApplySite, isMandatory: Bool) -> ApplySite? { let result = _bridged.tryDevirtualizeApply(apply.bridged, isMandatory) if let newApply = result.newApply.instruction { erase(instruction: apply) notifyInstructionsChanged() notifyCallsChanged() if result.cfgChanged { notifyBranchesChanged() } notifyInstructionChanged(newApply) return newApply as! FullApplySite } return nil } func inlineFunction(apply: FullApplySite, mandatoryInline: Bool) { // This is only a best-effort attempt to notity the new cloned instructions as changed. // TODO: get a list of cloned instructions from the `inlineFunction` let instAfterInling: Instruction? switch apply { case is ApplyInst: instAfterInling = apply.next case let beginApply as BeginApplyInst: let next = beginApply.next! instAfterInling = (next is EndApplyInst ? nil : next) case is TryApplyInst: instAfterInling = apply.parentBlock.next?.instructions.first default: instAfterInling = nil } _bridged.inlineFunction(apply.bridged, mandatoryInline) if let instAfterInling = instAfterInling { notifyNewInstructions(from: apply, to: instAfterInling) } } private func notifyNewInstructions(from: Instruction, to: Instruction) { var inst = from while inst != to { if !inst.isDeleted { notifyInstructionChanged(inst) } if let next = inst.next { inst = next } else { inst = inst.parentBlock.next!.instructions.first! } } } func getContextSubstitutionMap(for type: Type) -> SubstitutionMap { SubstitutionMap(_bridged.getContextSubstitutionMap(type.bridged)) } func notifyInstructionsChanged() { _bridged.asNotificationHandler().notifyChanges(.instructionsChanged) } func notifyCallsChanged() { _bridged.asNotificationHandler().notifyChanges(.callsChanged) } func notifyBranchesChanged() { _bridged.asNotificationHandler().notifyChanges(.branchesChanged) } /// Notifies the pass manager that the optimization result of the current pass depends /// on the body (i.e. SIL instructions) of another function than the currently optimized one. func notifyDependency(onBodyOf otherFunction: Function) { _bridged.notifyDependencyOnBodyOf(otherFunction.bridged) } } /// The context which is passed to the run-function of a FunctionPass. struct FunctionPassContext : MutatingContext { let _bridged: BridgedPassContext // A no-op. var notifyInstructionChanged: (Instruction) -> () { return { inst in } } func continueWithNextSubpassRun(for inst: Instruction? = nil) -> Bool { return _bridged.continueWithNextSubpassRun(inst.bridged) } func createSimplifyContext(preserveDebugInfo: Bool, notifyInstructionChanged: @escaping (Instruction) -> ()) -> SimplifyContext { SimplifyContext(_bridged: _bridged, notifyInstructionChanged: notifyInstructionChanged, preserveDebugInfo: preserveDebugInfo) } var aliasAnalysis: AliasAnalysis { let bridgedAA = _bridged.getAliasAnalysis() return AliasAnalysis(bridged: bridgedAA) } var deadEndBlocks: DeadEndBlocksAnalysis { let bridgeDEA = _bridged.getDeadEndBlocksAnalysis() return DeadEndBlocksAnalysis(bridged: bridgeDEA) } var dominatorTree: DominatorTree { let bridgedDT = _bridged.getDomTree() return DominatorTree(bridged: bridgedDT) } var postDominatorTree: PostDominatorTree { let bridgedPDT = _bridged.getPostDomTree() return PostDominatorTree(bridged: bridgedPDT) } var swiftArrayDecl: NominalTypeDecl { NominalTypeDecl(_bridged: _bridged.getSwiftArrayDecl()) } func loadFunction(name: StaticString, loadCalleesRecursively: Bool) -> Function? { return name.withUTF8Buffer { (nameBuffer: UnsafeBufferPointer) in let nameStr = BridgedStringRef(data: nameBuffer.baseAddress, count: nameBuffer.count) return _bridged.loadFunction(nameStr, loadCalleesRecursively).function } } func loadFunction(function: Function, loadCalleesRecursively: Bool) -> Bool { if function.isDefinition { return true } _bridged.loadFunction(function.bridged, loadCalleesRecursively) return function.isDefinition } /// Looks up a function in the `Swift` module. /// The `name` is the source name of the function and not the mangled name. /// Returns nil if no such function or multiple matching functions are found. func lookupStdlibFunction(name: StaticString) -> Function? { return name.withUTF8Buffer { (nameBuffer: UnsafeBufferPointer) in let nameStr = BridgedStringRef(data: nameBuffer.baseAddress, count: nameBuffer.count) return _bridged.lookupStdlibFunction(nameStr).function } } func modifyEffects(in function: Function, _ body: (inout FunctionEffects) -> ()) { notifyEffectsChanged() function._modifyEffects(body) } fileprivate func notifyEffectsChanged() { _bridged.asNotificationHandler().notifyChanges(.effectsChanged) } func optimizeMemoryAccesses(in function: Function) -> Bool { if _bridged.optimizeMemoryAccesses(function.bridged) { notifyInstructionsChanged() return true } return false } func eliminateDeadAllocations(in function: Function) -> Bool { if _bridged.eliminateDeadAllocations(function.bridged) { notifyInstructionsChanged() return true } return false } func specializeVTable(for type: Type, in function: Function) -> VTable? { guard let vtablePtr = _bridged.specializeVTableForType(type.bridged, function.bridged) else { return nil } return VTable(bridged: BridgedVTable(vTable: vtablePtr)) } func specializeClassMethodInst(_ cm: ClassMethodInst) -> Bool { if _bridged.specializeClassMethodInst(cm.bridged) { notifyInstructionsChanged() notifyCallsChanged() return true } return false } func specializeApplies(in function: Function, isMandatory: Bool) -> Bool { if _bridged.specializeAppliesInFunction(function.bridged, isMandatory) { notifyInstructionsChanged() notifyCallsChanged() return true } return false } func mangleOutlinedVariable(from function: Function) -> String { return String(taking: _bridged.mangleOutlinedVariable(function.bridged)) } func createGlobalVariable(name: String, type: Type, isPrivate: Bool) -> GlobalVariable { let gv = name._withBridgedStringRef { _bridged.createGlobalVariable($0, type.bridged, isPrivate) } return gv.globalVar } } struct SimplifyContext : MutatingContext { let _bridged: BridgedPassContext let notifyInstructionChanged: (Instruction) -> () let preserveDebugInfo: Bool } extension Type { func getStaticSize(context: SimplifyContext) -> Int? { let v = context._bridged.getStaticSize(self.bridged) return v == -1 ? nil : v } func getStaticAlignment(context: SimplifyContext) -> Int? { let v = context._bridged.getStaticAlignment(self.bridged) return v == -1 ? nil : v } func getStaticStride(context: SimplifyContext) -> Int? { let v = context._bridged.getStaticStride(self.bridged) return v == -1 ? nil : v } } //===----------------------------------------------------------------------===// // Builder initialization //===----------------------------------------------------------------------===// extension Builder { /// Creates a builder which inserts _before_ `insPnt`, using a custom `location`. init(before insPnt: Instruction, location: Location, _ context: some MutatingContext) { self.init(insertAt: .before(insPnt), location: location, context.notifyInstructionChanged, context._bridged.asNotificationHandler()) } /// Creates a builder which inserts _before_ `insPnt`, using the location of `insPnt`. init(before insPnt: Instruction, _ context: some MutatingContext) { self.init(insertAt: .before(insPnt), location: insPnt.location, context.notifyInstructionChanged, context._bridged.asNotificationHandler()) } /// Creates a builder which inserts _after_ `insPnt`, using a custom `location`. init(after insPnt: Instruction, location: Location, _ context: some MutatingContext) { if let nextInst = insPnt.next { self.init(insertAt: .before(nextInst), location: location, context.notifyInstructionChanged, context._bridged.asNotificationHandler()) } else { self.init(insertAt: .atEndOf(insPnt.parentBlock), location: location, context.notifyInstructionChanged, context._bridged.asNotificationHandler()) } } /// Creates a builder which inserts _after_ `insPnt`, using the location of `insPnt`. init(after insPnt: Instruction, _ context: some MutatingContext) { self.init(after: insPnt, location: insPnt.location, context) } /// Creates a builder which inserts at the end of `block`, using a custom `location`. init(atEndOf block: BasicBlock, location: Location, _ context: some MutatingContext) { self.init(insertAt: .atEndOf(block), location: location, context.notifyInstructionChanged, context._bridged.asNotificationHandler()) } /// Creates a builder which inserts at the begin of `block`, using a custom `location`. init(atBeginOf block: BasicBlock, location: Location, _ context: some MutatingContext) { let firstInst = block.instructions.first! self.init(insertAt: .before(firstInst), location: location, context.notifyInstructionChanged, context._bridged.asNotificationHandler()) } /// Creates a builder which inserts at the begin of `block`, using the location of the first /// instruction of `block`. init(atBeginOf block: BasicBlock, _ context: some MutatingContext) { let firstInst = block.instructions.first! self.init(insertAt: .before(firstInst), location: firstInst.location, context.notifyInstructionChanged, context._bridged.asNotificationHandler()) } init(staticInitializerOf global: GlobalVariable, _ context: some MutatingContext) { self.init(insertAt: .staticInitializer(global), location: Location.artificialUnreachableLocation, { _ in }, context._bridged.asNotificationHandler()) } } //===----------------------------------------------------------------------===// // Modifying the SIL //===----------------------------------------------------------------------===// extension Undef { static func get(type: Type, _ context: some MutatingContext) -> Undef { context._bridged.getSILUndef(type.bridged).value as! Undef } } extension BasicBlock { func addArgument(type: Type, ownership: Ownership, _ context: some MutatingContext) -> Argument { context.notifyInstructionsChanged() return bridged.addBlockArgument(type.bridged, ownership._bridged).argument } func eraseArgument(at index: Int, _ context: some MutatingContext) { context.notifyInstructionsChanged() bridged.eraseArgument(index) } func moveAllInstructions(toBeginOf otherBlock: BasicBlock, _ context: some MutatingContext) { context.notifyInstructionsChanged() context.notifyBranchesChanged() bridged.moveAllInstructionsToBegin(otherBlock.bridged) } func moveAllInstructions(toEndOf otherBlock: BasicBlock, _ context: some MutatingContext) { context.notifyInstructionsChanged() context.notifyBranchesChanged() bridged.moveAllInstructionsToEnd(otherBlock.bridged) } func eraseAllArguments(_ context: some MutatingContext) { // Arguments are stored in an array. We need to erase in reverse order to avoid quadratic complexity. for argIdx in (0 ..< arguments.count).reversed() { eraseArgument(at: argIdx, context) } } func moveAllArguments(to otherBlock: BasicBlock, _ context: some MutatingContext) { bridged.moveArgumentsTo(otherBlock.bridged) } } extension AllocRefInstBase { func setIsStackAllocatable(_ context: some MutatingContext) { context.notifyInstructionsChanged() bridged.AllocRefInstBase_setIsStackAllocatable() context.notifyInstructionChanged(self) } } extension Sequence where Element == Operand { func replaceAll(with replacement: Value, _ context: some MutatingContext) { for use in self { use.set(to: replacement, context) } } } extension Operand { func set(to value: Value, _ context: some MutatingContext) { instruction.setOperand(at: index, to: value, context) } } extension Instruction { func setOperand(at index : Int, to value: Value, _ context: some MutatingContext) { if let apply = self as? FullApplySite, apply.isCallee(operand: operands[index]) { context.notifyCallsChanged() } context.notifyInstructionsChanged() bridged.setOperand(index, value.bridged) context.notifyInstructionChanged(self) } func move(before otherInstruction: Instruction, _ context: some MutatingContext) { BridgedPassContext.moveInstructionBefore(bridged, otherInstruction.bridged) context.notifyInstructionsChanged() } } extension BuiltinInst { func constantFold(_ context: some MutatingContext) -> Value? { context._bridged.constantFoldBuiltin(bridged).value } } extension RefCountingInst { func setAtomicity(isAtomic: Bool, _ context: some MutatingContext) { context.notifyInstructionsChanged() bridged.RefCountingInst_setIsAtomic(isAtomic) context.notifyInstructionChanged(self) } } extension AllocRefInst { func setIsBare(_ context: some MutatingContext) { context.notifyInstructionsChanged() bridged.AllocRefInst_setIsBare() context.notifyInstructionChanged(self) } } extension RefElementAddrInst { func set(isImmutable: Bool, _ context: some MutatingContext) { context.notifyInstructionsChanged() bridged.RefElementAddrInst_setImmutable(isImmutable) context.notifyInstructionChanged(self) } } extension GlobalAddrInst { func clearToken(_ context: some MutatingContext) { context.notifyInstructionsChanged() bridged.GlobalAddrInst_clearToken() context.notifyInstructionChanged(self) } } extension GlobalValueInst { func setIsBare(_ context: some MutatingContext) { context.notifyInstructionsChanged() bridged.GlobalValueInst_setIsBare() context.notifyInstructionChanged(self) } } extension LoadInst { func set(ownership: LoadInst.LoadOwnership, _ context: some MutatingContext) { context.notifyInstructionsChanged() bridged.LoadInst_setOwnership(ownership.rawValue) context.notifyInstructionChanged(self) } } extension TermInst { func replaceBranchTarget(from fromBlock: BasicBlock, to toBlock: BasicBlock, _ context: some MutatingContext) { context.notifyBranchesChanged() bridged.TermInst_replaceBranchTarget(fromBlock.bridged, toBlock.bridged) } } extension ForwardingInstruction { func setForwardingOwnership(to ownership: Ownership, _ context: some MutatingContext) { context.notifyInstructionsChanged() bridged.ForwardingInst_setForwardingOwnership(ownership._bridged) } } extension Function { func set(needStackProtection: Bool, _ context: FunctionPassContext) { context.notifyEffectsChanged() bridged.setNeedStackProtection(needStackProtection) } func fixStackNesting(_ context: FunctionPassContext) { context._bridged.fixStackNesting(bridged) } }