Skip to content

Commit 3128eae

Browse files
committed
Add NestedSemanticFunctionCheck diagnostic
to check for improperly nested '@_semantic' functions. Add a missing @_semantics("array.init") in ArraySlice found by the diagnostic. Distinguish between array.init and array.init.empty. Categorize the types of semantic functions by how they affect the inliner and pass pipeline, and centralize this logic in PerformanceInlinerUtils. The ultimate goal is to prevent inlining of "Fundamental" @_semantics calls and @_effects calls until the late pipeline where we can safely discard semantics. However, that requires significant pipeline changes. In the meantime, this change prevents the situation from getting worse and makes the intention clear. However, it has no significant effect on the pass pipeline and inliner.
1 parent 0991d8d commit 3128eae

File tree

17 files changed

+323
-79
lines changed

17 files changed

+323
-79
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 & 0 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:

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)