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();
}
|