File: SILSkippingChecker.cpp

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (147 lines) | stat: -rw-r--r-- 5,308 bytes parent folder | download
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
143
144
145
146
147
//===---------------------- SILSkippingChecker.cpp ------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "swift/AST/Module.h"
#include "swift/Basic/LLVM.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"

using namespace swift;

/// Determines whether we should have skipped SILGenning a function that
/// appears in a SILModule. This only applies when
/// \c -experimental-skip-non-inlinable-function-bodies is enabled.
static bool shouldHaveSkippedFunction(const SILFunction &F) {
  assert(F.getModule().getOptions().SkipFunctionBodies ==
             FunctionBodySkipping::NonInlinable &&
         "this check only makes sense if we're skipping function bodies");

  // First, we only care about functions that haven't been marked serialized.
  // If they've been marked serialized, they will end up in the final module
  // and we needed to SILGen them.
  if (F.isAnySerialized())
    return false;

  // Next, we're looking for functions that shouldn't have a body, but do. If
  // the function doesn't have a body (i.e. it's an external declaration), we
  // skipped it successfully.
  if (F.isExternalDeclaration())
    return false;

  // Thunks and specializations are automatically synthesized, so it's fine that
  // they end up in the module.
  if (F.isThunk() || F.isSpecialization())
    return false;

  // FIXME: We can probably skip property initializers, too.
  auto func = F.getLocation().getAsASTNode<AbstractFunctionDecl>();
  if (!func)
    return false;

  // If a body is synthesized/implicit, it shouldn't be skipped.
  if (func->isImplicit())
    return false;

  // Local function bodies in inlinable code are okay to show up in the module.
  if (func->getDeclContext()->isLocalContext())
    return false;

  // FIXME: Identify __ivar_destroyer, __allocating_init, and
  //        __deallocating_deinit, which have no special marking, are always
  //        emitted, and do have a source location with the original decl
  //        attached.
  if (isa<DestructorDecl>(func) || isa<ConstructorDecl>(func))
    return false;

  // Some AccessorDecls can't be skipped (see IsFunctionBodySkippedRequest).
  if (auto *AD = dyn_cast<AccessorDecl>(func)) {
    if (AD->getAccessorKind() == AccessorKind::DidSet)
      return false;

    if (AD->hasForcedStaticDispatch())
      return false;
  }

  // Functions with @backDeployed may be copied into the client, so they
  // shouldn't be skipped. The SILFunction that may be copied into the client
  // should be serialized and therefore is already handled above. However, a
  // second resilient SILFunction is also emitted for back deployed functions.
  // Since the body of the function as written was not skipped, it's expected
  // that we see the SILFunction for the resilient copy here.
  if (func->hasBackDeployedAttr())
    return false;

  // If none of those conditions trip, then this is something that _should_
  // be serialized in the module even when we're skipping non-inlinable
  // function bodies.
  return true;
}

namespace {

/// This is a verification utility pass that's meant to be used with
/// \c -experimental-skip-non-inlinable-function-bodies or
/// \c -experimental-skip-all-function-bodies. It checks that SIL is only
/// generated for what's actually serialized.all functions
/// that aren't serialized also have no generated SIL.
class SILSkippingChecker : public SILModuleTransform {
  void run() override {
    auto &M = *getModule();

    // Skip this verification for SwiftOnoneSupport
    if (M.getSwiftModule()->isOnoneSupportModule())
      return;

    if (M.getOptions().SkipFunctionBodies == FunctionBodySkipping::All) {
      // Should only have an empty SIL module
      bool notEmpty = false;
      if (!M.getFunctions().empty())
        notEmpty = true;
      if (!M.getVTables().empty())
        notEmpty = true;
      if (!M.getWitnessTables().empty())
        notEmpty = true;
      if (!M.getSILGlobals().empty())
        notEmpty = true;

      if (notEmpty) {
        llvm::dbgs() << "Non-empty SIL module when all function bodies should "
                        "have been skipped!\n";
        abort();
      }
    }

    if (M.getOptions().SkipFunctionBodies ==
        FunctionBodySkipping::NonInlinable) {
      for (auto &F : *getModule()) {
        if (!shouldHaveSkippedFunction(F))
          continue;

        llvm::dbgs() << "Function serialized that should have been skipped!\n";
        F.getLocation().print(llvm::dbgs(), F.getModule().getSourceManager());
        llvm::dbgs() << "\n";
        F.dump();
        abort();
      }
    }
  }
};

} // end anonymous namespace

SILTransform *swift::createSILSkippingChecker() {
  return new SILSkippingChecker();
}