-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathNestedSemanticFunctionCheck.cpp
142 lines (131 loc) · 4.99 KB
/
NestedSemanticFunctionCheck.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//===--- NestedSemanticFunctionCheck.cpp - Disallow nested @_semantic -----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 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
//
//===----------------------------------------------------------------------===//
///
/// This pass raises a diagnostic error if any semantic functions in the current
/// module are improperly nested. A semantic function has an @_semantic
/// attribute. Semantic functions may call other semantic functions, but
/// semantic and non-semantic call frames may not be interleaved.
///
/// @_semantics(...)
/// func funcA() {
/// funcB()
/// }
/// // Error: funcB is called by a semantic function and calls
/// // a semantic function.
/// func funcB() {
/// funcC()
/// }
/// @_semantics(...)
/// func funcC() {}
///
/// For the pass pipeline to function well, @_semantic calls need to be
/// processed top-down, while inlining proceeds bottom-up. With proper nesting,
/// this is easily accomplished, but with improper nesting, semantic function
/// can be prematurely "hidden".
///
/// For simplicity, don't bother checking whether semantic functions in one
/// module call non-semantic functions in a different module. Really,
/// "optimizable" @_semantics should only exist in the standard library.
///
/// TODO: The @_semantics tag should only ever be used to convey function
/// semantics that are important to optimizations and which cannot be determined
/// from the function body. Make sure this tag is never used for compiler hints
/// or informational attributes, which do not require special pass pipeline and
/// module serialization behavior.
///
//===----------------------------------------------------------------------===//
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/PerformanceInlinerUtils.h"
using namespace swift;
namespace {
class NestedSemanticFunctionCheck : public SILFunctionTransform {
// Mark all functions that may indirectly call a semantic function, ignoring
// simple wrappers, but are not themselves an optimizable semantic function.
//
// This could be a very large set. It is built gradually during bottom-up
// function transforms, then deleted once all functions are processed before
// executing the next pass pipeline.
llvm::SmallPtrSet<SILFunction *, 8> mayCallSemanticFunctions;
// Mark semantic functions and wrappers around semantic calls.
llvm::SmallPtrSet<SILFunction *, 8> semanticFunctions;
public:
void run() override;
protected:
void checkSemanticFunction(SILFunction *f);
};
} // end anonymous namespace
/// This is a semantic function. Diagnose any calls to mayCallSemanticFunctions.
void NestedSemanticFunctionCheck::checkSemanticFunction(SILFunction *f) {
for (auto &bb : *f) {
for (auto &i : bb) {
auto apply = FullApplySite::isa(&i);
if (!apply) {
continue;
}
auto callee = apply.getReferencedFunctionOrNull();
if (!callee) {
continue;
}
// Semantic function calling non-semantic function with improperly
// nested semantic calls underneath.
if (mayCallSemanticFunctions.count(callee)) {
ASTContext &astContext = f->getASTContext();
astContext.Diags.diagnose(apply.getLoc().getSourceLoc(),
diag::semantic_function_improper_nesting);
}
}
}
}
// If this is a semantic function, diagnose it immediately. Otherwise propagate
// the mayCallSemanticFunctions and semanticCallWrappers sets upward in the call
// tree.
void NestedSemanticFunctionCheck::run() {
auto *f = getFunction();
if (!semanticFunctions.count(f) && isOptimizableSemanticFunction(f)) {
semanticFunctions.insert(f);
checkSemanticFunction(f);
return;
}
// Add this function to mayCallSemanticFunctions if needed.
if (mayCallSemanticFunctions.count(f)) {
return;
}
for (auto &bb : *f) {
for (auto &i : bb) {
auto apply = FullApplySite::isa(&i);
if (!apply) {
continue;
}
// If this is a trivial wrapper around a semantic call, don't add it
// immediately to the mayCallSemanticFunctions set, but add it to
// semanticCallWrappers so that its caller will be added to
// mayCallSemanticFunctions.
if (isNestedSemanticCall(apply)) {
semanticFunctions.insert(f);
continue;
}
auto callee = apply.getReferencedFunctionOrNull();
if (!callee) {
continue;
}
// Propagate mayCallSemanticFunctions.
if (mayCallSemanticFunctions.count(callee)
|| semanticFunctions.count(callee)) {
mayCallSemanticFunctions.insert(f);
}
}
}
}
SILTransform *swift::createNestedSemanticFunctionCheck() {
return new NestedSemanticFunctionCheck();
}