Skip to content

Commit e2ee007

Browse files
authored
Merge pull request #81159 from eeckstein/deinit-barrier-optimizations
Some small deinit-barrier related optimizations
2 parents 6b4710e + 2fc32fb commit e2ee007

File tree

11 files changed

+140
-11
lines changed

11 files changed

+140
-11
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ let computeSideEffects = FunctionPass(name: "compute-side-effects") {
7070

7171
// Finally replace the function's side effects.
7272
context.modifyEffects(in: function) { (effects: inout FunctionEffects) in
73-
effects.sideEffects = SideEffects(arguments: collectedEffects.argumentEffects, global: collectedEffects.globalEffects)
73+
let globalEffects = function.isProgramTerminationPoint ?
74+
collectedEffects.globalEffects.forProgramTerminationPoints
75+
: collectedEffects.globalEffects
76+
effects.sideEffects = SideEffects(arguments: collectedEffects.argumentEffects, global: globalEffects)
7477
}
7578
}
7679

SwiftCompilerSources/Sources/SIL/Effects.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,7 @@ extension Function {
161161
break
162162
}
163163
if isProgramTerminationPoint {
164-
// We can ignore any memory writes in a program termination point, because it's not relevant
165-
// for the caller. But we need to consider memory reads, otherwise preceding memory writes
166-
// would be eliminated by dead-store-elimination in the caller. E.g. String initialization
167-
// for error strings which are printed by the program termination point.
168-
// Regarding ownership: a program termination point must not touch any reference counted objects.
169-
return SideEffects.GlobalEffects(memory: SideEffects.Memory(read: true))
164+
return SideEffects.GlobalEffects.worstEffects.forProgramTerminationPoints
170165
}
171166
var result = SideEffects.GlobalEffects.worstEffects
172167
switch effectAttribute {
@@ -568,6 +563,18 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren {
568563
return result
569564
}
570565

566+
/// Effects with all effects removed which are not relevant for program termination points (like `fatalError`).
567+
public var forProgramTerminationPoints: GlobalEffects {
568+
// We can ignore any memory writes in a program termination point, because it's not relevant
569+
// for the caller. But we need to consider memory reads, otherwise preceding memory writes
570+
// would be eliminated by dead-store-elimination in the caller. E.g. String initialization
571+
// for error strings which are printed by the program termination point.
572+
// Regarding ownership: a program termination point must not touch any reference counted objects.
573+
// Also, the deinit-barrier effect is not relevant because functions like `fatalError` and `exit` are
574+
// not accessing objects (except strings).
575+
return GlobalEffects(memory: Memory(read: memory.read))
576+
}
577+
571578
public static var worstEffects: GlobalEffects {
572579
GlobalEffects(memory: .worstEffects, ownership: .worstEffects, allocates: true, isDeinitBarrier: true)
573580
}

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,10 @@ extension Function {
576576
atIndex: calleeArgIdx,
577577
withConvention: convention)
578578
return effects.memory.read
579+
},
580+
// isDeinitBarrier
581+
{ (f: BridgedFunction) -> Bool in
582+
return f.function.getSideEffects().isDeinitBarrier
579583
}
580584
)
581585
}

include/swift/SIL/SILBridging.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,14 +549,16 @@ struct BridgedFunction {
549549
typedef EffectInfo (* _Nonnull GetEffectInfoFn)(BridgedFunction, SwiftInt);
550550
typedef BridgedMemoryBehavior (* _Nonnull GetMemBehaviorFn)(BridgedFunction, bool);
551551
typedef bool (* _Nonnull ArgumentMayReadFn)(BridgedFunction, BridgedOperand, BridgedValue);
552+
typedef bool (* _Nonnull IsDeinitBarrierFn)(BridgedFunction);
552553

553554
static void registerBridging(SwiftMetatype metatype,
554555
RegisterFn initFn, RegisterFn destroyFn,
555556
WriteFn writeFn, ParseFn parseFn,
556557
CopyEffectsFn copyEffectsFn,
557558
GetEffectInfoFn effectInfoFn,
558559
GetMemBehaviorFn memBehaviorFn,
559-
ArgumentMayReadFn argumentMayReadFn);
560+
ArgumentMayReadFn argumentMayReadFn,
561+
IsDeinitBarrierFn isDeinitBarrierFn);
560562
};
561563

562564
struct OptionalBridgedFunction {

include/swift/SIL/SILFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,8 @@ class SILFunction
12181218
// Used by the MemoryLifetimeVerifier
12191219
bool argumentMayRead(Operand *argOp, SILValue addr);
12201220

1221+
bool isDeinitBarrier();
1222+
12211223
Purpose getSpecialPurpose() const { return specialPurpose; }
12221224

12231225
/// Get this function's global_init attribute.

lib/SIL/IR/SILFunction.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ static BridgedFunction::CopyEffectsFn copyEffectsFunction = nullptr;
214214
static BridgedFunction::GetEffectInfoFn getEffectInfoFunction = nullptr;
215215
static BridgedFunction::GetMemBehaviorFn getMemBehvaiorFunction = nullptr;
216216
static BridgedFunction::ArgumentMayReadFn argumentMayReadFunction = nullptr;
217+
static BridgedFunction::IsDeinitBarrierFn isDeinitBarrierFunction = nullptr;
217218

218219
SILFunction::SILFunction(
219220
SILModule &Module, SILLinkage Linkage, StringRef Name,
@@ -1175,7 +1176,8 @@ void BridgedFunction::registerBridging(SwiftMetatype metatype,
11751176
CopyEffectsFn copyEffectsFn,
11761177
GetEffectInfoFn effectInfoFn,
11771178
GetMemBehaviorFn memBehaviorFn,
1178-
ArgumentMayReadFn argumentMayReadFn) {
1179+
ArgumentMayReadFn argumentMayReadFn,
1180+
IsDeinitBarrierFn isDeinitBarrierFn) {
11791181
functionMetatype = metatype;
11801182
initFunction = initFn;
11811183
destroyFunction = destroyFn;
@@ -1185,6 +1187,7 @@ void BridgedFunction::registerBridging(SwiftMetatype metatype,
11851187
getEffectInfoFunction = effectInfoFn;
11861188
getMemBehvaiorFunction = memBehaviorFn;
11871189
argumentMayReadFunction = argumentMayReadFn;
1190+
isDeinitBarrierFunction = isDeinitBarrierFn;
11881191
}
11891192

11901193
std::pair<const char *, int> SILFunction::
@@ -1286,6 +1289,13 @@ bool SILFunction::argumentMayRead(Operand *argOp, SILValue addr) {
12861289
return argumentMayReadFunction({this}, {argOp}, {addr});
12871290
}
12881291

1292+
bool SILFunction::isDeinitBarrier() {
1293+
if (!isDeinitBarrierFunction)
1294+
return true;
1295+
1296+
return isDeinitBarrierFunction({this});
1297+
}
1298+
12891299
SourceFile *SILFunction::getSourceFile() const {
12901300
auto declRef = getDeclRef();
12911301
if (!declRef)

lib/SILOptimizer/Utils/SILInliner.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,9 @@ void SILInlineCloner::cloneInline(ArrayRef<SILValue> AppliedArgs) {
500500
if (!enableLexicalLifetimes)
501501
continue;
502502

503+
if (!Original.isDeinitBarrier())
504+
continue;
505+
503506
// Exclusive mutating accesses don't entail a lexical scope.
504507
if (paramInfo.getConvention() == ParameterConvention::Indirect_Inout)
505508
continue;

lib/Serialization/SerializeSIL.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,16 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) {
529529

530530
unsigned numAttrs = NoBody ? 0 : F.getSpecializeAttrs().size();
531531
auto resilience = F.getModule().getSwiftModule()->getResilienceStrategy();
532-
bool serializeDerivedEffects = (resilience != ResilienceStrategy::Resilient) &&
533-
!F.hasSemanticsAttr("optimize.no.crossmodule");
532+
bool serializeDerivedEffects =
533+
// We must not serialize computed effects if library evolution is turned on,
534+
// because the copy of the function, which is emitted into the current module,
535+
// might have different effects in different versions of the library.
536+
(resilience != ResilienceStrategy::Resilient ||
537+
// But we can serialize computed effects for @alwaysEmitIntoClient functions,
538+
// even when library evolution is enabled, because no copy of the function is
539+
// emitted in the original module.
540+
F.getLinkage() == SILLinkage::PublicNonABI) &&
541+
!F.hasSemanticsAttr("optimize.no.crossmodule");
534542

535543
F.visitArgEffects(
536544
[&](int effectIdx, int argumentIndex, bool isDerived) {

test/SIL/Serialization/effects.sil

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -module-name=NonLE -emit-module -o %t/NonLE.swiftmodule
3+
// RUN: %target-swift-frontend %s -module-name=LE -enable-library-evolution -emit-module -o %t/LE.swiftmodule
4+
// RUN: %target-sil-opt %t/NonLE.swiftmodule | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NONLE
5+
// RUN: %target-sil-opt %t/LE.swiftmodule | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-LE
6+
7+
sil_stage canonical
8+
9+
import Swift
10+
import Builtin
11+
12+
// CHECK-LABEL: sil [serialized] [canonical] [ossa] @public_func :
13+
// CHECK-NONLE-NEXT: [global: ]
14+
// CHECK-NEXT: bb0:
15+
sil [serialized] [ossa] @public_func : $@convention(thin) () -> () {
16+
[global: ]
17+
bb0:
18+
%r = tuple ()
19+
return %r
20+
}
21+
22+
// CHECK-LABEL: sil non_abi [serialized] [canonical] [ossa] @public_non_abi_func :
23+
// CHECK-NEXT: [global: ]
24+
// CHECK-NEXT: bb0:
25+
sil non_abi [serialized] [ossa] @public_non_abi_func : $@convention(thin) () -> () {
26+
[global: ]
27+
bb0:
28+
%r = tuple ()
29+
return %r
30+
}
31+

test/SILOptimizer/mandatory_inlining_ownership.sil

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,41 @@ bb3:
619619
return %9999 : $()
620620
}
621621

622+
sil [transparent] [ossa] @deinit_barrier : $@convention(thin) (@in_guaranteed Int) -> () {
623+
[global: deinit_barrier]
624+
bb0(%0 : $*Int):
625+
%1 = tuple ()
626+
return %1
627+
}
628+
629+
sil [transparent] [ossa] @no_deinit_barrier : $@convention(thin) (@in_guaranteed Int) -> () {
630+
[global: ]
631+
bb0(%0 : $*Int):
632+
%1 = tuple ()
633+
return %1
634+
}
635+
636+
// CHECK-LABEL: sil [ossa] @inline_deinit_barrier :
637+
// CHECK: alloc_stack [lexical] $Int
638+
// CHECK: alloc_stack $Int
639+
// CHECK: } // end sil function 'inline_deinit_barrier'
640+
sil [ossa] @inline_deinit_barrier : $@convention(thin) (Int) -> () {
641+
bb0(%0 : $Int):
642+
%1 = alloc_stack $Int
643+
store %0 to [trivial] %1
644+
%2 = function_ref @deinit_barrier : $@convention(thin) (@in_guaranteed Int) -> ()
645+
apply %2(%1) : $@convention(thin) (@in_guaranteed Int) -> ()
646+
dealloc_stack %1
647+
648+
%10 = alloc_stack $Int
649+
store %0 to [trivial] %10
650+
%12 = function_ref @no_deinit_barrier : $@convention(thin) (@in_guaranteed Int) -> ()
651+
apply %12(%10) : $@convention(thin) (@in_guaranteed Int) -> ()
652+
dealloc_stack %10
653+
654+
%r = tuple ()
655+
return %r
656+
}
622657

623658
sil_vtable C2 {
624659
#C2.i!modify: (C2) -> () -> () : @devirt_callee

test/SILOptimizer/side_effects.sil

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,13 @@ bb0(%0 : $*X):
680680
return %r : $()
681681
}
682682

683+
sil [_semantics "programtermination_point"] @exitfunc_defined : $@convention(thin) () -> Never {
684+
bb0:
685+
%u = function_ref @unknown_func : $@convention(thin) () -> ()
686+
%a = apply %u() : $@convention(thin) () -> ()
687+
unreachable
688+
}
689+
683690
// CHECK-LABEL: sil @call_noreturn
684691
// CHECK-NEXT: [global: read]
685692
// CHECK-NEXT: {{^[^[]}}
@@ -697,6 +704,23 @@ bb2:
697704
return %r : $()
698705
}
699706

707+
// CHECK-LABEL: sil @call_defined_noreturn
708+
// CHECK-NEXT: [global: read]
709+
// CHECK-NEXT: {{^[^[]}}
710+
sil @call_defined_noreturn : $@convention(thin) () -> () {
711+
bb0:
712+
cond_br undef, bb1, bb2
713+
714+
bb1:
715+
%u = function_ref @exitfunc_defined : $@convention(thin) () -> Never
716+
%a = apply %u() : $@convention(thin) () -> Never
717+
unreachable
718+
719+
bb2:
720+
%r = tuple ()
721+
return %r : $()
722+
}
723+
700724
// CHECK-LABEL: sil @call_readnone
701725
// CHECK-NEXT: [global: copy,deinit_barrier]
702726
// CHECK-NEXT: {{^[^[]}}

0 commit comments

Comments
 (0)