Skip to content

Add some instruction simplifications to make precondition() not allocate in Onone builds #69401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ swift_compiler_sources(Optimizer
SimplifyBuiltin.swift
SimplifyCondBranch.swift
SimplifyCondFail.swift
SimplifyConvertEscapeToNoEscape.swift
SimplifyCopyValue.swift
SimplifyDebugStep.swift
SimplifyDestroyValue.swift
SimplifyDestructure.swift
SimplifyGlobalValue.swift
SimplifyInitEnumDataAddr.swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import SIL

extension ApplyInst : OnoneSimplifyable {
func simplify(_ context: SimplifyContext) {
if tryTransformThickToThinCallee(of: self, context) {
return
}
_ = context.tryDevirtualize(apply: self, isMandatory: false)
}
}
Expand All @@ -29,3 +32,29 @@ extension BeginApplyInst : OnoneSimplifyable {
_ = context.tryDevirtualize(apply: self, isMandatory: false)
}
}

/// Optimizes a thick function call if the callee is a `thin_to_thick_function` instruction:
///
/// %2 = thin_to_thick_function %1
/// %3 = apply %2(...) : @callee_guaranteed
/// ->
/// %2 = thin_to_thick_function %1
/// %3 = apply %1(...): @convention(thin)
///
private func tryTransformThickToThinCallee(of apply: ApplyInst, _ context: SimplifyContext) -> Bool {
if let tttf = apply.callee as? ThinToThickFunctionInst,
!apply.callee.type.isCalleeConsumedFunction
{
let builder = Builder(before: apply, context)
let newApply = builder.createApply(function: tttf.operand.value,
apply.substitutionMap,
arguments: Array(apply.arguments),
isNonThrowing: apply.isNonThrowing,
isNonAsync: apply.isNonAsync,
specializationInfo: apply.specializationInfo)
apply.uses.replaceAll(with: newApply, context)
context.erase(instruction: apply)
return true
}
return false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===--- SimplifyConvertEscapeToNoEscape.swift ----------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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

extension ConvertEscapeToNoEscapeInst : OnoneSimplifyable {
func simplify(_ context: SimplifyContext) {
tryCombineWithThinToThickOperand(context)
}
}

private extension ConvertEscapeToNoEscapeInst {

/// Combine with a thin_to_thick_function operand:
///
/// %2 = thin_to_thick_function %1 to $() -> ()
/// %3 = convert_escape_to_noescape %2 : $() -> () to $@noescape () -> ()
/// ->
/// %3 = thin_to_thick_function %1 to $@noescape () -> ()

func tryCombineWithThinToThickOperand(_ context: SimplifyContext) {
if let thinToThick = fromFunction as? ThinToThickFunctionInst {
let builder = Builder(before: self, context)
let noEscapeFnType = thinToThick.type.getFunctionType(withNoEscape: true)
let newThinToThick = builder.createThinToThickFunction(thinFunction: thinToThick.operand.value,
resultType: noEscapeFnType)
uses.replaceAll(with: newThinToThick, context)
context.erase(instruction: self)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//===--- SimplifyCopyValue.swift ------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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

extension CopyValueInst : OnoneSimplifyable, SILCombineSimplifyable {
func simplify(_ context: SimplifyContext) {
if fromValue.ownership == .none {
uses.replaceAll(with: fromValue, context)
context.erase(instruction: self)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===--- SimplifyDestroyValue.swift ---------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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

extension DestroyValueInst : OnoneSimplifyable, SILCombineSimplifyable {
func simplify(_ context: SimplifyContext) {
if destroyedValue.ownership == .none {
context.erase(instruction: self)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ private func registerSwiftPasses() {
registerForSILCombine(RetainValueInst.self, { run(RetainValueInst.self, $0) })
registerForSILCombine(ReleaseValueInst.self, { run(ReleaseValueInst.self, $0) })
registerForSILCombine(LoadInst.self, { run(LoadInst.self, $0) })
registerForSILCombine(CopyValueInst.self, { run(CopyValueInst.self, $0) })
registerForSILCombine(DestroyValueInst.self, { run(DestroyValueInst.self, $0) })

// Test passes
registerPass(functionUsesDumper, { functionUsesDumper.run($0) })
Expand Down
5 changes: 5 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Builder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ public struct Builder {
return notifyNew(enumInst.getAs(EnumInst.self))
}

public func createThinToThickFunction(thinFunction: Value, resultType: Type) -> ThinToThickFunctionInst {
let tttf = bridged.createThinToThickFunction(thinFunction.bridged, resultType.bridged)
return notifyNew(tttf.getAs(ThinToThickFunctionInst.self))
}

@discardableResult
public func createSwitchEnum(enum enumVal: Value,
cases: [(Int, BasicBlock)],
Expand Down
5 changes: 5 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,11 @@ class ConvertFunctionInst : SingleValueInstruction, ConversionInstruction {
final public
class ThinToThickFunctionInst : SingleValueInstruction, ConversionInstruction {}

final public
class ConvertEscapeToNoEscapeInst : SingleValueInstruction, UnaryInstruction {
public var fromFunction: Value { operand.value }
}

final public
class ObjCExistentialMetatypeToObjectInst : SingleValueInstruction,
ConversionInstruction {}
Expand Down
1 change: 1 addition & 0 deletions SwiftCompilerSources/Sources/SIL/Registration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public func registerSILClasses() {
register(UnconditionalCheckedCastInst.self)
register(ConvertFunctionInst.self)
register(ThinToThickFunctionInst.self)
register(ConvertEscapeToNoEscapeInst.self)
register(ObjCExistentialMetatypeToObjectInst.self)
register(ObjCMetatypeToObjectInst.self)
register(ValueToBridgeObjectInst.self)
Expand Down
4 changes: 4 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ public struct Type : CustomStringConvertible, NoReflectionChildren {
return idx >= 0 ? idx : nil
}

public func getFunctionType(withNoEscape: Bool) -> Type {
bridged.getFunctionTypeWithNoEscape(withNoEscape).type
}

public var description: String {
String(taking: bridged.getDebugDescription())
}
Expand Down
3 changes: 3 additions & 0 deletions include/swift/SIL/SILBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ struct BridgedType {
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedStringRef getFieldName(SwiftInt idx) const;
BRIDGED_INLINE SwiftInt getNumTupleElements() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedType getTupleElementType(SwiftInt idx) const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedType getFunctionTypeWithNoEscape(bool withNoEscape) const;
};

// AST bridging
Expand Down Expand Up @@ -899,6 +900,8 @@ struct BridgedBuilder{
SwiftInt caseIdx, BridgedType resultType) const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createEnum(SwiftInt caseIdx, OptionalBridgedValue payload,
BridgedType resultType) const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createThinToThickFunction(BridgedValue fn,
BridgedType resultType) const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createBranch(BridgedBasicBlock destBlock,
BridgedValueArray arguments) const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createUnreachable() const;
Expand Down
10 changes: 10 additions & 0 deletions include/swift/SIL/SILBridgingImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ BridgedType BridgedType::getTupleElementType(SwiftInt idx) const {
return get().getTupleElementType(idx);
}

BridgedType BridgedType::getFunctionTypeWithNoEscape(bool withNoEscape) const {
auto fnType = get().getAs<swift::SILFunctionType>();
auto newTy = fnType->getWithExtInfo(fnType->getExtInfo().withNoEscape(true));
return swift::SILType::getPrimitiveObjectType(newTy);
}

//===----------------------------------------------------------------------===//
// BridgedNominalTypeDecl
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1352,6 +1358,10 @@ BridgedInstruction BridgedBuilder::createEnum(SwiftInt caseIdx, OptionalBridgedV
return {get().createEnum(regularLoc(), pl, caseDecl, resultType.get())};
}

BridgedInstruction BridgedBuilder::createThinToThickFunction(BridgedValue fn, BridgedType resultType) const {
return {get().createThinToThickFunction(regularLoc(), fn.getSILValue(), resultType.get())};
}

BridgedInstruction BridgedBuilder::createBranch(BridgedBasicBlock destBlock, BridgedValueArray arguments) const {
llvm::SmallVector<swift::SILValue, 16> argValues;
return {get().createBranch(regularLoc(), destBlock.get(), arguments.getValues(argValues))};
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SILOptimizer/PassManager/Passes.def
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,8 @@ SWIFT_SILCOMBINE_PASS(StrongReleaseInst)
SWIFT_SILCOMBINE_PASS(RetainValueInst)
SWIFT_SILCOMBINE_PASS(ReleaseValueInst)
SWIFT_SILCOMBINE_PASS(LoadInst)
SWIFT_SILCOMBINE_PASS(CopyValueInst)
SWIFT_SILCOMBINE_PASS(DestroyValueInst)

#undef IRGEN_PASS
#undef SWIFT_MODULE_PASS
Expand Down
2 changes: 0 additions & 2 deletions lib/SILOptimizer/SILCombiner/SILCombiner.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,6 @@ class SILCombiner :
SILInstruction *optimizeStringObject(BuiltinInst *BI);
SILInstruction *visitBuiltinInst(BuiltinInst *BI);
SILInstruction *visitCondFailInst(CondFailInst *CFI);
SILInstruction *visitCopyValueInst(CopyValueInst *cvi);
SILInstruction *visitDestroyValueInst(DestroyValueInst *dvi);
SILInstruction *visitRefToRawPointerInst(RefToRawPointerInst *RRPI);
SILInstruction *visitUpcastInst(UpcastInst *UCI);

Expand Down
31 changes: 0 additions & 31 deletions lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -865,37 +865,6 @@ SILInstruction *SILCombiner::visitCondFailInst(CondFailInst *CFI) {
return nullptr;
}

SILInstruction *SILCombiner::visitCopyValueInst(CopyValueInst *cvi) {
assert(cvi->getFunction()->hasOwnership());

// Sometimes when RAUWing code we get copy_value on .none values (consider
// transformations around function types that result in given a copy_value a
// thin_to_thick_function argument). In such a case, just RAUW with the
// copy_value's operand since it is a no-op.
if (cvi->getOperand()->getOwnershipKind() == OwnershipKind::None) {
replaceInstUsesWith(*cvi, cvi->getOperand());
return eraseInstFromFunction(*cvi);
}

return nullptr;
}

SILInstruction *SILCombiner::visitDestroyValueInst(DestroyValueInst *dvi) {
assert(dvi->getFunction()->hasOwnership());

// Sometimes when RAUWing code we get destroy_value on .none values. In such a
// case, just delete the destroy_value.
//
// As an example, consider transformations around function types that result
// in a thin_to_thick_function being passed to a destroy_value.
if (dvi->getOperand()->getOwnershipKind() == OwnershipKind::None) {
eraseInstFromFunction(*dvi);
return nullptr;
}

return nullptr;
}

/// Create a value from stores to an address.
///
/// If there are only stores to \p addr, return the stored value. Also, if there
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/big_types_corner_cases.sil
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -I %S/Inputs/abi %s -emit-ir | %FileCheck %s
// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -I %S/Inputs/abi %s -emit-ir | %FileCheck %s

// REQUIRES: CPU=x86_64
// REQUIRES: OS=macosx
Expand Down
2 changes: 1 addition & 1 deletion test/SILGen/reabstract.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

// RUN: %target-swift-emit-silgen -module-name reabstract -Xllvm -sil-full-demangle %s | %FileCheck %s
// RUN: %target-swift-emit-sil -module-name reabstract -Xllvm -sil-full-demangle %s | %FileCheck %s --check-prefix=MANDATORY
// RUN: %target-swift-emit-sil -module-name reabstract -Xllvm -sil-disable-pass=simplification -Xllvm -sil-full-demangle %s | %FileCheck %s --check-prefix=MANDATORY

func closureTakingOptional(_ fn: (Int?) -> ()) {}
closureTakingOptional({ (_: Any) -> () in })
Expand Down
2 changes: 1 addition & 1 deletion test/SILGen/rethrows.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

// RUN: %target-swift-emit-sil -module-name rethrows -verify %s | %FileCheck %s
// RUN: %target-swift-emit-sil -module-name rethrows -Xllvm -sil-disable-pass=simplification -verify %s | %FileCheck %s

@discardableResult
func rethrower(_ fn: () throws -> Int) rethrows -> Int {
Expand Down
1 change: 0 additions & 1 deletion test/SILOptimizer/definite-init-convert-to-escape.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ public func returnOptionalEscape() -> (() ->())?
// CHECK: [[BLOCK_PROJ:%.*]] = project_block_storage [[BLOCK_SLOT]]
// CHECK: store [[MDI]] to [[BLOCK_PROJ]]
// CHECK: [[BLOCK:%.*]] = init_block_storage_header [[BLOCK_SLOT]]
// CHECK: release_value [[NONE]]
// CHECK: [[SOME_2:%.*]] = enum $Optional<{{.*}}>, #Optional.some!enumelt, [[MDI]]
// CHECK: [[BLOCK_COPY:%.*]] = copy_block [[BLOCK]]
// CHECK: [[BLOCK_SOME:%.*]] = enum $Optional<{{.*}}>, #Optional.some!enumelt, [[BLOCK_COPY]]
Expand Down
3 changes: 2 additions & 1 deletion test/SILOptimizer/definite_init_value_types.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// RUN: %target-swift-frontend -enable-copy-propagation=requested-passes-only -enable-lexical-lifetimes=false -emit-sil %s | %FileCheck %s

// REQUIRES: swift_in_compiler

enum ValueEnum {
case a(String)
case b
Expand Down Expand Up @@ -57,7 +59,6 @@ enum ValueEnum {
// CHECK: bb6:
// CHECK-NEXT: [[NEW_STATE:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-NEXT: store [[NEW_STATE]] to [[STATE]]
// CHECK-NEXT: retain_value [[NEW_SELF]]
// CHECK-NEXT: store [[NEW_SELF]] to [[SELF_ACCESS]]
// CHECK-NEXT: end_access [[SELF_ACCESS]]
// CHECK-NEXT: destroy_addr [[SELF_BOX]]
Expand Down
3 changes: 1 addition & 2 deletions test/SILOptimizer/mandatory_inlining_reasync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ func reasyncFunction(_ value: Optional<Int>, _ fn: () async throws -> Int) reasy

// CHECK-LABEL: sil hidden @$s26mandatory_inlining_reasync20callsReasyncFunctionSiyF : $@convention(thin) () -> Int {
// CHECK: [[FN:%.*]] = function_ref @$s26mandatory_inlining_reasync20callsReasyncFunctionSiyFSiyXEfU_ : $@convention(thin) () -> Int
// CHECK-NEXT: [[THICK:%.*]] = thin_to_thick_function [[FN]] : $@convention(thin) () -> Int to $@noescape @callee_guaranteed () -> Int
// CHECK-NEXT: [[RESULT:%.*]] = apply [[THICK]]() : $@noescape @callee_guaranteed () -> Int
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]() : $@convention(thin) () -> Int
// CHECK-NEXT: return [[RESULT]] : $Int
func callsReasyncFunction() -> Int {
return reasyncFunction(nil, { return 321 } )
Expand Down
5 changes: 5 additions & 0 deletions test/SILOptimizer/performance-annotations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -412,3 +412,8 @@ func testInfiniteLoop(_ c: Cl) {
while true {}
}

@_noAllocation
func testPrecondition(_ count: Int) {
precondition(count == 2, "abc")
}

14 changes: 14 additions & 0 deletions test/SILOptimizer/simplify_apply.sil
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ class Bar {
func foo() -> Int
}

sil @cl : $@convention(thin) () -> Int

// CHECK-LABEL: sil [ossa] @thick_to_thin :
// CHECK: [[F:%.*]] = function_ref @cl
// CHECK: apply [[F]]() : $@convention(thin
// CHECK: } // end sil function 'thick_to_thin'
sil [ossa] @thick_to_thin : $@convention(thin) () -> Int {
bb0:
%0 = function_ref @cl : $@convention(thin) () -> Int
%1 = thin_to_thick_function %0 : $@convention(thin) () -> Int to $@callee_guaranteed () -> Int
%2 = apply %1() : $@callee_guaranteed () -> Int
return %2 : $Int
}

// CHECK-LABEL: sil @devirt_class_method :
// CHECK: [[F:%.*]] = function_ref @bar_foo
// CHECK: apply [[F]]
Expand Down
28 changes: 28 additions & 0 deletions test/SILOptimizer/simplify_convert_escape_to_noescape.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=convert_escape_to_noescape | %FileCheck %s

// REQUIRES: swift_in_compiler

sil_stage canonical

import Builtin
import Swift
import SwiftShims

sil @cl : $@convention(thin) () -> Int

// CHECK-LABEL: sil [ossa] @remove_convert :
// CHECK: [[F:%.*]] = thin_to_thick_function %0 : $@convention(thin) () -> Int to $@noescape @callee_guaranteed () -> Int
// CHECK-NEXT: apply [[F]]() : $@noescape
// CHECK-NEXT: destroy_value [[F]]
// CHECK: } // end sil function 'remove_convert'
sil [ossa] @remove_convert : $@convention(thin) () -> Int {
bb0:
%0 = function_ref @cl : $@convention(thin) () -> Int
%1 = thin_to_thick_function %0 : $@convention(thin) () -> Int to $@callee_guaranteed () -> Int
%2 = convert_escape_to_noescape %1 : $@callee_guaranteed () -> Int to $@noescape @callee_guaranteed () -> Int
%3 = apply %2() : $@noescape @callee_guaranteed () -> Int
destroy_value %2 : $@noescape @callee_guaranteed () -> Int
return %3 : $Int
}


Loading