Skip to content

Commit 5be168a

Browse files
authored
Merge pull request swiftlang#34430 from atrick/check-nested-semantics
Add NestedSemanticFunctionCheck warning diagnostic
2 parents 64e5f76 + 72029d4 commit 5be168a

20 files changed

+344
-154
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,5 +643,7 @@ ERROR(box_to_stack_cannot_promote_box_to_stack_due_to_escape_alloc, none,
643643
NOTE(box_to_stack_cannot_promote_box_to_stack_due_to_escape_location, none,
644644
"value escapes here", ())
645645

646+
WARNING(semantic_function_improper_nesting, none, "'@_semantics' function calls non-'@_semantics' function with nested '@_semantics' calls", ())
647+
646648
#define UNDEFINE_DIAGNOSTIC_MACROS
647649
#include "DefineDiagnosticMacros.h"

include/swift/SILOptimizer/Analysis/ArraySemantic.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ enum class ArrayCallKind {
4242
// a function, and it has a self parameter, make sure that it is defined
4343
// before this comment.
4444
kArrayInit,
45+
kArrayInitEmpty,
4546
kArrayUninitialized,
4647
kArrayUninitializedIntrinsic,
4748
kArrayFinalizeIntrinsic
@@ -187,7 +188,7 @@ class ArraySemanticsCall {
187188

188189
/// Could this array be backed by an NSArray.
189190
bool mayHaveBridgedObjectElementType() const;
190-
191+
191192
/// Can this function be inlined by the early inliner.
192193
bool canInlineEarly() const;
193194

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ PASS(Outliner, "outliner",
251251
"Function Outlining Optimization")
252252
PASS(OwnershipModelEliminator, "ownership-model-eliminator",
253253
"Eliminate Ownership Annotation of SIL")
254+
PASS(NestedSemanticFunctionCheck, "nested-semantic-function-check",
255+
"Diagnose improperly nested '@_semantics' functions")
254256
PASS(NonTransparentFunctionOwnershipModelEliminator,
255257
"non-transparent-func-ownership-model-eliminator",
256258
"Eliminate Ownership Annotations from non-transparent SIL Functions")

include/swift/SILOptimizer/Utils/PerformanceInlinerUtils.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,38 @@ enum class InlineSelection {
3939
OnlyInlineAlways,
4040
};
4141

42-
// Returns the callee of an apply_inst if it is basically inlinable.
42+
/// Check if this ApplySite is eligible for inlining. If so, return the callee.
4343
SILFunction *getEligibleFunction(FullApplySite AI,
4444
InlineSelection WhatToInline);
4545

4646
// Returns true if this is a pure call, i.e. the callee has no side-effects
4747
// and all arguments are constants.
4848
bool isPureCall(FullApplySite AI, SideEffectAnalysis *SEA);
49+
50+
/// Fundamental @_semantic tags provide additional semantics for optimization
51+
/// beyond what SIL analysis can discover from the function body. They should be
52+
/// preserved until semantic passes can process them. For example, they may need
53+
/// to be hoisted by LICM after inlining up to the highest level caller.
54+
///
55+
/// Nested @_semantic tags also provide semantics beyond the function body but
56+
/// they hide the semantics of underlying calls. These need to be inlined into
57+
/// the highest level caller before semantics passes can process the underlying
58+
/// Fundamental semantic functions.
59+
///
60+
/// Transient @_semantic tags are not required for optimization.
61+
enum class SemanticFunctionLevel { Fundamental, Transient, Nested };
62+
63+
/// Return the SemanticFunctionLevel of \p callee.
64+
SemanticFunctionLevel getSemanticFunctionLevel(SILFunction *function);
65+
66+
inline bool isOptimizableSemanticFunction(SILFunction *function) {
67+
return getSemanticFunctionLevel(function) != SemanticFunctionLevel::Transient;
68+
}
69+
70+
/// Return true if \p apply calls into an optimizable semantic function from
71+
/// within another semantic function, or from a "trivial" wrapper.
72+
bool isNestedSemanticCall(FullApplySite apply);
73+
4974
} // end swift namespace
5075

5176
//===----------------------------------------------------------------------===//

lib/SILOptimizer/Analysis/ArraySemantic.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ ArrayCallKind swift::getArraySemanticsKind(SILFunction *f) {
3030
llvm::StringSwitch<ArrayCallKind>(Attrs)
3131
.Case("array.props.isNativeTypeChecked",
3232
ArrayCallKind::kArrayPropsIsNativeTypeChecked)
33-
.StartsWith("array.init", ArrayCallKind::kArrayInit)
33+
.Case("array.init", ArrayCallKind::kArrayInit)
34+
.Case("array.init.empty", ArrayCallKind::kArrayInitEmpty)
3435
.Case("array.uninitialized", ArrayCallKind::kArrayUninitialized)
3536
.Case("array.uninitialized_intrinsic", ArrayCallKind::kArrayUninitializedIntrinsic)
3637
.Case("array.finalize_intrinsic", ArrayCallKind::kArrayFinalizeIntrinsic)
@@ -682,8 +683,10 @@ static SILValue getArrayUninitializedInitResult(ArraySemanticsCall arrayCall,
682683

683684
SILValue swift::ArraySemanticsCall::getArrayValue() const {
684685
ArrayCallKind arrayCallKind = getKind();
685-
if (arrayCallKind == ArrayCallKind::kArrayInit)
686+
if (arrayCallKind == ArrayCallKind::kArrayInit
687+
|| arrayCallKind == ArrayCallKind::kArrayInitEmpty) {
686688
return SILValue(SemanticsCall);
689+
}
687690
return getArrayUninitializedInitResult(*this, 0);
688691
}
689692

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,6 +1943,7 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
19431943
!isa<BeginApplyInst>(I)) {
19441944
ArraySemanticsCall ASC(FAS.getInstruction());
19451945
switch (ASC.getKind()) {
1946+
// TODO: Model ReserveCapacityForAppend, AppendContentsOf, AppendElement.
19461947
case ArrayCallKind::kArrayPropsIsNativeTypeChecked:
19471948
case ArrayCallKind::kCheckSubscript:
19481949
case ArrayCallKind::kCheckIndex:
@@ -2023,36 +2024,6 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
20232024
break;
20242025
}
20252026

2026-
if (FAS.getReferencedFunctionOrNull() &&
2027-
FAS.getReferencedFunctionOrNull()->hasSemanticsAttr(
2028-
"self_no_escaping_closure") &&
2029-
((FAS.hasIndirectSILResults() && FAS.getNumArguments() == 3) ||
2030-
(!FAS.hasIndirectSILResults() && FAS.getNumArguments() == 2)) &&
2031-
FAS.hasSelfArgument()) {
2032-
// The programmer has guaranteed that the closure will not capture the
2033-
// self pointer passed to it or anything that is transitively reachable
2034-
// from the pointer.
2035-
auto Args = FAS.getArgumentsWithoutIndirectResults();
2036-
// The first not indirect result argument is the closure.
2037-
ConGraph->setEscapesGlobal(Args[0]);
2038-
return;
2039-
}
2040-
2041-
if (FAS.getReferencedFunctionOrNull() &&
2042-
FAS.getReferencedFunctionOrNull()->hasSemanticsAttr(
2043-
"pair_no_escaping_closure") &&
2044-
((FAS.hasIndirectSILResults() && FAS.getNumArguments() == 4) ||
2045-
(!FAS.hasIndirectSILResults() && FAS.getNumArguments() == 3)) &&
2046-
FAS.hasSelfArgument()) {
2047-
// The programmer has guaranteed that the closure will not capture the
2048-
// self pointer passed to it or anything that is transitively reachable
2049-
// from the pointer.
2050-
auto Args = FAS.getArgumentsWithoutIndirectResults();
2051-
// The second not indirect result argument is the closure.
2052-
ConGraph->setEscapesGlobal(Args[1]);
2053-
return;
2054-
}
2055-
20562027
if (RecursionDepth < MaxRecursionDepth) {
20572028
CalleeList Callees = BCA->getCalleeList(FAS);
20582029
if (buildConnectionGraphForCallees(FAS.getInstruction(), Callees, FInfo,

lib/SILOptimizer/IPO/GlobalPropertyOpt.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ void GlobalPropertyOpt::scanInstruction(swift::SILInstruction *Inst) {
269269
ArraySemanticsCall semCall(AI);
270270
switch (semCall.getKind()) {
271271
case ArrayCallKind::kArrayInit:
272+
case ArrayCallKind::kArrayInitEmpty:
272273
case ArrayCallKind::kArrayUninitialized:
273274
case ArrayCallKind::kMutateUnknown:
274275
case ArrayCallKind::kMakeMutable:

lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ static bool isNonMutatingArraySemanticCall(SILInstruction *Inst) {
295295
case ArrayCallKind::kReserveCapacityForAppend:
296296
case ArrayCallKind::kWithUnsafeMutableBufferPointer:
297297
case ArrayCallKind::kArrayInit:
298+
case ArrayCallKind::kArrayInitEmpty:
298299
case ArrayCallKind::kArrayUninitialized:
299300
case ArrayCallKind::kArrayUninitializedIntrinsic:
300301
case ArrayCallKind::kArrayFinalizeIntrinsic:
@@ -658,6 +659,7 @@ bool COWArrayOpt::hasLoopOnlyDestructorSafeArrayOperations() {
658659
auto Kind = Sem.getKind();
659660
// Safe because they create new arrays.
660661
if (Kind == ArrayCallKind::kArrayInit ||
662+
Kind == ArrayCallKind::kArrayInitEmpty ||
661663
Kind == ArrayCallKind::kArrayUninitialized ||
662664
Kind == ArrayCallKind::kArrayUninitializedIntrinsic)
663665
continue;

lib/SILOptimizer/LoopTransforms/LoopUnroll.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ static bool canAndShouldUnrollLoop(SILLoop *Loop, uint64_t TripCount) {
194194
auto Callee = AI.getCalleeFunction();
195195
if (Callee && getEligibleFunction(AI, InlineSelection::Everything)) {
196196
// If callee is rather big and potentialy inlinable, it may be better
197-
// not to unroll, so that the body of the calle can be inlined later.
197+
// not to unroll, so that the body of the callee can be inlined later.
198198
Cost += Callee->size() * InsnsPerBB;
199199
}
200200
}

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ target_sources(swiftSILOptimizer PRIVATE
1414
Differentiation.cpp
1515
IRGenPrepare.cpp
1616
MandatoryInlining.cpp
17+
NestedSemanticFunctionCheck.cpp
1718
PredictableMemOpt.cpp
1819
PMOMemoryUseCollector.cpp
1920
RawSILInstLowering.cpp

0 commit comments

Comments
 (0)