Merge pull request #23943 from gottesmm/pr-5f0e58774d6cdc3eea4498b84255f8293e0bd665

diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt
index 1479ab9..6063c62 100644
--- a/benchmark/CMakeLists.txt
+++ b/benchmark/CMakeLists.txt
@@ -253,6 +253,10 @@
     "-whole-module-optimization" "-num-threads" "4")
 set(BENCHOPTS_SINGLEFILE "")
 
+option(SWIFT_BENCHMARK_USE_OS_LIBRARIES
+  "Runtime link against the Swift libraries on the target (/usr/lib/swift)."
+  FALSE)
+
 configure_build()
 
 #===-----------------------------------------------------------------------===#
@@ -268,10 +272,18 @@
 message("--")
 message("-- Swift Benchmark Suite:")
 message("--   SWIFT_BENCHMARK_BUILT_STANDALONE = ${SWIFT_BENCHMARK_BUILT_STANDALONE}")
-message("--   SWIFT_EXEC = ${SWIFT_EXEC}")
-message("--   SWIFT_BENCHMARK_EXTRA_FLAGS = ${SWIFT_BENCHMARK_EXTRA_FLAGS}")
+message("--   SWIFT_BENCHMARK_USE_OS_LIBRARIES = ${SWIFT_BENCHMARK_USE_OS_LIBRARIES}")
+message("--   SWIFT_EXEC         = ${SWIFT_EXEC}")
 message("--   SWIFT_LIBRARY_PATH = ${SWIFT_LIBRARY_PATH}")
-message("--   CLANG_EXEC = ${CLANG_EXEC}")
+if (SWIFT_RPATH_BASE)
+message("--   SWIFT_RPATH_BASE   = ${SWIFT_RPATH_BASE}")
+endif()
+if (SWIFT_RPATH)
+message("--   SWIFT_RPATH        = ${SWIFT_RPATH}")
+message("---  ** WARNING ** Benchmarking against Swift-in-the-OS")
+endif()
+message("--   CLANG_EXEC         = ${CLANG_EXEC}")
+message("--   SWIFT_BENCHMARK_EXTRA_FLAGS = ${SWIFT_BENCHMARK_EXTRA_FLAGS}")
 message("--   SWIFT_OPTIMIZATION_LEVELS = ${SWIFT_OPTIMIZATION_LEVELS}")
 message("--   ONLY_PLATFORMS = ${ONLY_PLATFORMS}")
 message("--   PAGE_ALIGNMENT_OPTION = ${PAGE_ALIGNMENT_OPTION}")
diff --git a/benchmark/README.md b/benchmark/README.md
index 44ba62b..657863f 100644
--- a/benchmark/README.md
+++ b/benchmark/README.md
@@ -28,6 +28,11 @@
 OS X benchmark driver binaries are placed in `bin` alongside `swiftc`.
 Additional platform binaries are placed in the `benchmark/bin` build directory.
 
+The required Swift standard library dylibs are placed in `lib`. The
+drivers dynamically link Swift standard library dylibs from a path
+relative to their run-time location (../lib/swift) so the standard
+library should be distributed alongside them.
+
 Building Independently
 ----------------------
 
@@ -43,15 +48,19 @@
 * `-DSWIFT_LIBRARY_PATH`
     * An absolute path to the Swift standard library to use during compilation
       (default: `swiftc_directory`/../lib/swift)
+* `-DSWIFT_DARWIN_XCRUN_TOOLCHAIN`
+    * The Xcode toolchain to use when invoking `xcrun` to find `clang`.
+      (default: XcodeDefault)
 * `-DONLY_PLATFORMS`
     * A list of platforms to build the benchmarks for
       (default: "macosx;iphoneos;appletvos;watchos")
 * `-DSWIFT_OPTIMIZATION_LEVELS`
     * A list of Swift optimization levels to build against
       (default: "O;Onone;Osize")
-* `-DSWIFT_BENCHMARK_EMIT_SIB`
-    * A boolean value indicating whether .sib files should be generated
-      alongside .o files (default: FALSE)
+* `-DSWIFT_BENCHMARK_USE_OS_LIBRARIES`
+    * Enable this option to link the benchmark binaries against the target
+      machine's Swift standard library and runtime installed with the OS.
+    (default: OFF)
 
 The following build targets are available:
 
@@ -66,13 +75,40 @@
 1. `$ cd benchmark`
 2. `$ mkdir build`
 3. `$ cd build`
-4. `$ cmake ..`
-5. `$ make -j8 swift-benchmark-macosx-x86_64`
+4. `$ cmake ../benchmark -G Ninja -DSWIFT_EXEC=[path to built swiftc]`
+5. `$ ninja swift-benchmark-macosx-x86_64`
 
-Benchmark driver binaries are placed in `build/bin` and the required Swift
-standard library dylibs are placed in `build/lib`. The drivers dynamically link
-Swift standard library dylibs from a path relative to their location
-(../lib/swift) so the standard library should be distributed alongside them.
+Benchmark binaries are placed in `bin`.
+
+The binaries dynamically link Swift standard library dylibs from a
+path determined by the configuration. If `SWIFT_LIBRARY_PATH` is set,
+they link against the absolute path provided, regardless of where the
+binaries are installed. Otherwise, the runtime library path is
+relative to the benchmark binary at the time it was executed
+(`@executable_path/../lib/swift/<platform>`).
+
+For example, to benchmark against a locally built `swiftc`, including
+any standard library changes in that build, you might configure using:
+
+    cmake ../benchmark -G Ninja -DSWIFT_EXEC=<src>/swift/build/swift-macosx-x86_64/bin/swiftc
+    ninja swift-benchmark-iphoneos-arm64
+
+To build against the installed Xcode, simply omit SWIFT_EXEC:
+
+    cmake ../benchmark -G Ninja
+    ninja swift-benchmark-iphoneos-arm64
+
+In both examples above, to run the benchmarks on a device, the dynamic
+libraries must then be copied onto the device into the library path
+relative to `swiftc`. To benchmark against the target machine's
+installed libraries instead, enable
+`SWIFT_BENCHMARK_USE_OS_LIBRARIES`.
+
+    cmake ../benchmark -G Ninja -DSWIFT_BENCHMARK_USE_OS_LIBRARIES=ON
+    ninja swift-benchmark-iphoneos-arm64
+
+This will reflect the performance of the Swift standard library
+installed on the device, not the one included in the Swift root.
 
 Using the Benchmark Driver
 --------------------------
diff --git a/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake
index 953eeae..bc05c8f 100644
--- a/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake
+++ b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake
@@ -24,6 +24,9 @@
     if(${c_compiler} STREQUAL "clang")
       set(CLANG_EXEC ${CMAKE_C_COMPILER})
     else()
+      if(NOT SWIFT_DARWIN_XCRUN_TOOLCHAIN)
+        set(SWIFT_DARWIN_XCRUN_TOOLCHAIN "XcodeDefault")
+      endif()
       runcmd(COMMAND "xcrun" "-toolchain" "${SWIFT_DARWIN_XCRUN_TOOLCHAIN}" "-f" "clang"
              VARIABLE CLANG_EXEC
              ERROR "Unable to find Clang driver")
@@ -57,12 +60,47 @@
     endif()
   endif()
 
-  # We always infer the SWIFT_LIBRARY_PATH from SWIFT_EXEC unless
-  # SWIFT_LIBRARY_PATH is specified explicitly.
-  if(NOT SWIFT_LIBRARY_PATH)
-    get_filename_component(tmp_dir "${SWIFT_EXEC}" DIRECTORY)
-    get_filename_component(tmp_dir "${tmp_dir}" DIRECTORY)
-    set(SWIFT_LIBRARY_PATH "${tmp_dir}/lib/swift")
+  # Set LIBRARY_PATH and either RPATH or RPATH_BASE. To build and run
+  # for multiple platforms, RPATH_BASE must be set instead of RPATH. It
+  # is the platform-independent runtime library directory. The platform
+  # subdirectory name will be appended to form a different RPATH for
+  # on platform.
+
+  # If requested, use Swift-in-the-OS. This way, the benchmarks may be built
+  # standalone on the host, and the binaries can run directly from a temp dir
+  # on any target machine. Of course, this factors out performance changes in
+  # stdlib or overlays.
+  if(SWIFT_BENCHMARK_USE_OS_LIBRARIES)
+    set(SWIFT_RPATH "/usr/lib/swift")
+  endif()
+
+  # When SWIFT_LIBRARY_PATH is specified explicitly for a standalone
+  # build, use it as an absolute RPATH_BASE. This only works when
+  # running benchmarks on the host machine. Otherwise, RPATH is set
+  # assuming that libraries will be installed later (manually)
+  # relative to the benchmark binaries.
+  #
+  # When not building standalone, SWIFT_LIBRARY_PATH is set by LLVM
+  # cmake to the build directory for Swift dylibs. Otherwise, assume
+  # that the dylibs are built relative to SWIFT_EXEC.
+  if(SWIFT_LIBRARY_PATH AND SWIFT_BENCHMARK_BUILT_STANDALONE)
+    if (NOT SWIFT_RPATH)
+      set(SWIFT_RPATH_BASE ${SWIFT_LIBRARY_PATH})
+    endif()
+  else()
+    if (NOT SWIFT_LIBRARY_PATH)
+      get_filename_component(tmp_dir "${SWIFT_EXEC}" DIRECTORY)
+      get_filename_component(tmp_dir "${tmp_dir}" DIRECTORY)
+      set(SWIFT_LIBRARY_PATH "${tmp_dir}/lib/swift")
+    endif()
+    if (NOT SWIFT_RPATH)
+      # If the benchmarks are built against a local swift build, assume that
+      # either the benchmarks will be installed in the swift build dir,
+      # or the swift libraries will be installed in the benchmark location in
+      # a platform specific subdirectory.
+      # This way, performance always factors in changes to the libraries.
+      set(SWIFT_RPATH_BASE "@executable_path/../lib/swift")
+    endif()
   endif()
 endmacro()
 
@@ -571,6 +609,11 @@
   # both do exactly the same thing with both sets of arguments. It also lets us
   # avoid issues around code-signing.
   if (is_darwin)
+    if (SWIFT_RPATH)
+      set(SWIFT_LINK_RPATH "${SWIFT_RPATH}")
+    else()
+      set(SWIFT_LINK_RPATH "${SWIFT_RPATH_BASE}/${BENCH_COMPILE_ARCHOPTS_PLATFORM}")
+    endif()
     add_custom_command(
         OUTPUT "${OUTPUT_EXEC}"
         DEPENDS
@@ -593,7 +636,7 @@
           "-lobjc"
           "-L${SWIFT_LIBRARY_PATH}/${BENCH_COMPILE_ARCHOPTS_PLATFORM}"
           "-Xlinker" "-rpath"
-          "-Xlinker" "@executable_path/../lib/swift/${BENCH_COMPILE_ARCHOPTS_PLATFORM}"
+          "-Xlinker" "${SWIFT_LINK_RPATH}"
           ${bench_library_objects}
           ${bench_driver_objects}
           ${SWIFT_BENCH_OBJFILES}
@@ -612,7 +655,7 @@
           "${SWIFT_EXEC}"
           "-O"
           "-target" "${target}"
-          "-L${SWIFT_LIBRARY_PATH}/${BENCH_COMPILE_ARCHOPTS_PLATFORM}"
+          "-L${SWIFT_LIBRARY_PATH}"
           ${bench_library_objects}
           ${bench_driver_objects}
           ${SWIFT_BENCH_OBJFILES}
diff --git a/docs/WindowsBuild.md b/docs/WindowsBuild.md
index afc01ac..0f6d4fd 100644
--- a/docs/WindowsBuild.md
+++ b/docs/WindowsBuild.md
@@ -359,6 +359,7 @@
 ```cmd
 md S:\b\llbuild
 cd S:\b\llbuild
+set AR=llvm-ar
 cmake -G Ninja^
   -DCMAKE_BUILD_TYPE=RelWithDebInfo^
   -DCMAKE_C_COMPILER=cl^
diff --git a/include/swift/SILOptimizer/Utils/PerformanceInlinerUtils.h b/include/swift/SILOptimizer/Utils/PerformanceInlinerUtils.h
index 27da047..6a6e221 100644
--- a/include/swift/SILOptimizer/Utils/PerformanceInlinerUtils.h
+++ b/include/swift/SILOptimizer/Utils/PerformanceInlinerUtils.h
@@ -312,6 +312,7 @@
   SILLoopInfo *LI;
   llvm::DenseMap<const SILBasicBlock *, BlockInfo *> BlockInfos;
   std::vector<BlockInfo> BlockInfoStorage;
+  bool valid = false;
 
   BlockInfo *getBlockInfo(const SILBasicBlock *BB) {
     BlockInfo *BI = BlockInfos[BB];
@@ -381,15 +382,22 @@
 public:
   ShortestPathAnalysis(SILFunction *F, SILLoopInfo *LI) : F(F), LI(LI) { }
 
-  bool isValid() const { return !BlockInfos.empty(); }
+  bool isValid() const { return valid; }
 
   /// Compute the distances. The function \p getApplyLength returns the length
   /// of a function call.
   template <typename Func>
   void analyze(ColdBlockInfo &CBI, Func getApplyLength) {
     assert(!isValid());
+    valid = true;
+    unsigned numBlocks = F->size();
 
-    BlockInfoStorage.resize(F->size());
+    // As the complexity of the analysis is more than linear with the number of blocks,
+    // disable it for huge functions. In this case inlining will be less aggressive.
+    if (numBlocks > 2000)
+      return;
+
+    BlockInfoStorage.resize(numBlocks);
 
     // First step: compute the length of the blocks.
     unsigned BlockIdx = 0;
@@ -433,6 +441,11 @@
   /// shortest path in the function.
   int getScopeLength(SILBasicBlock *BB, int LoopDepth) {
     assert(BB->getParent() == F);
+
+    // Return a conservative default if the analysis was not done due to a high number of blocks.
+    if (BlockInfos.empty())
+      return ColdBlockLength;
+
     if (LoopDepth >= MaxNumLoopLevels)
       LoopDepth = MaxNumLoopLevels - 1;
     return getBlockInfo(BB)->getScopeLength(LoopDepth);
diff --git a/lib/SIL/LinearLifetimeChecker.cpp b/lib/SIL/LinearLifetimeChecker.cpp
index 661fff4..09d630f 100644
--- a/lib/SIL/LinearLifetimeChecker.cpp
+++ b/lib/SIL/LinearLifetimeChecker.cpp
@@ -486,6 +486,25 @@
   // have been detected by initializing our consuming uses. So we are done.
   if (consumingUses.size() == 1 &&
       consumingUses[0].getParent() == value->getParentBlock()) {
+    // Check if any of our non consuming uses are not in the parent block. We
+    // flag those as additional use after frees. Any in the same block, we would
+    // have flagged.
+    if (llvm::any_of(nonConsumingUses, [&](BranchPropagatedUser user) {
+          return user.getParent() != value->getParentBlock();
+        })) {
+      state.error.handleUseAfterFree([&] {
+        llvm::errs() << "Function: '" << value->getFunction()->getName()
+                     << "'\n"
+                     << "Found use after free due to unvisited non lifetime "
+                        "ending uses?!\n"
+                     << "Value: " << *value << "    Remaining Users:\n";
+        for (const auto &user : nonConsumingUses) {
+          llvm::errs() << "User: " << *user.getInst();
+        }
+        llvm::errs() << "\n";
+      });
+    }
+
     return state.error;
   }
 
diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp
index 638af9a..2a77459 100644
--- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp
+++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp
@@ -81,6 +81,10 @@
 
     ConstantFolder ConstFolder;
 
+    // True if the function has a large amount of blocks. In this case we turn off some expensive
+    // optimizations.
+    bool isVeryLargeFunction = false;
+
     void constFoldingCallback(SILInstruction *I) {
       // If a terminal instruction gets constant folded (like cond_br), it
       // enables further simplify-CFG optimizations.
@@ -1226,11 +1230,13 @@
       if (DestBB->getArgument(i) != BI->getArg(i)) {
         SILValue Val = BI->getArg(i);
         DestBB->getArgument(i)->replaceAllUsesWith(Val);
-        if (auto *I = dyn_cast<SingleValueInstruction>(Val)) {
-          // Replacing operands may trigger constant folding which then could
-          // trigger other simplify-CFG optimizations.
-          ConstFolder.addToWorklist(I);
-          ConstFolder.processWorkList();
+        if (!isVeryLargeFunction) {
+          if (auto *I = dyn_cast<SingleValueInstruction>(Val)) {
+            // Replacing operands may trigger constant folding which then could
+            // trigger other simplify-CFG optimizations.
+            ConstFolder.addToWorklist(I);
+            ConstFolder.processWorkList();
+          }
         }
       } else {
         // We must be processing an unreachable part of the cfg with a cycle.
@@ -1290,7 +1296,7 @@
   // If this unconditional branch has BBArgs, check to see if duplicating the
   // destination would allow it to be simplified.  This is a simple form of jump
   // threading.
-  if (!BI->getArgs().empty() &&
+  if (!isVeryLargeFunction && !BI->getArgs().empty() &&
       tryJumpThreading(BI))
     return true;
 
@@ -3067,6 +3073,9 @@
 
   LLVM_DEBUG(llvm::dbgs() << "### Run SimplifyCFG on " << Fn.getName() << '\n');
 
+  // Disable some expensive optimizations if the function is huge.
+  isVeryLargeFunction = (Fn.size() > 10000);
+
   // First remove any block not reachable from the entry.
   bool Changed = removeUnreachableBlocks(Fn);
 
diff --git a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp
index 25ba688..72724f4 100644
--- a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp
+++ b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp
@@ -418,6 +418,10 @@
 getWeight(SILBasicBlock *BB, Weight CallerWeight) {
   assert(BB->getParent() == F);
 
+  // Return a conservative default if the analysis was not done due to a high number of blocks.
+  if (BlockInfos.empty())
+    return Weight(CallerWeight.ScopeLength + ColdBlockLength, CallerWeight.LoopWeight);
+
   SILLoop *Loop = LI->getLoopFor(BB);
   if (!Loop) {
     // We are not in a loop. So just account the length of our function scope
diff --git a/test/SIL/ownership-verifier/over_consume.sil b/test/SIL/ownership-verifier/over_consume.sil
index 7cd4fbf..42a12b1 100644
--- a/test/SIL/ownership-verifier/over_consume.sil
+++ b/test/SIL/ownership-verifier/over_consume.sil
@@ -22,6 +22,13 @@
 case none
 }
 
+protocol Error {}
+
+struct NativeObjectPair {
+  var obj1 : Builtin.NativeObject
+  var obj2 : Builtin.NativeObject
+}
+
 class SuperKlass {
   func doSomething()
 }
@@ -485,3 +492,35 @@
   %5 = tuple(%3 : $ClassProt, %4 : $ClassProt)
   return %5 : $(ClassProt, ClassProt)
 }
+
+sil [ossa] @eliminate_copy_try_apple_callee : $@convention(thin) (@owned Builtin.NativeObject) -> @error Error {
+entry(%0 : @owned $Builtin.NativeObject):
+  %9999 = tuple()
+  return %9999 : $()
+}
+
+
+// CHECK-LABEL: Function: 'use_after_free_consume_in_same_block'
+// CHECK: Found use after free due to unvisited non lifetime ending uses?!
+// CHECK: Value:   %3 = copy_value %2 : $Builtin.NativeObject
+// CHECK:     Remaining Users:
+// CHECK: User:   %10 = apply %7(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
+sil [ossa] @use_after_free_consume_in_same_block : $@convention(thin) (@owned NativeObjectPair) -> @error Error {
+bb0(%0 : @owned $NativeObjectPair):
+  %1 = begin_borrow %0 : $NativeObjectPair
+  %2 = struct_extract %1 : $NativeObjectPair, #NativeObjectPair.obj1
+  %3 = copy_value %2 : $Builtin.NativeObject
+  end_borrow %1 : $NativeObjectPair
+  destroy_value %0 : $NativeObjectPair
+  %4 = function_ref @eliminate_copy_try_apple_callee : $@convention(thin) (@owned Builtin.NativeObject) -> @error Error
+  %5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
+  try_apply %4(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> @error Error, normal bb1, error bb2
+
+bb1(%errorEmptyTup: $()):
+  apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
+  %9999 = tuple()
+  return %9999 : $()
+
+bb2(%error : @owned $Error):
+  throw %error : $Error
+}
diff --git a/test/lit.cfg b/test/lit.cfg
index 945a8b6..b6cd795 100644
--- a/test/lit.cfg
+++ b/test/lit.cfg
@@ -170,9 +170,12 @@
 
 # Choose between lit's internal shell pipeline runner and a real shell.  If
 # LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override.
-use_lit_shell = os.environ.get('LIT_USE_INTERNAL_SHELL', not kIsWindows)
+use_lit_shell = os.environ.get('LIT_USE_INTERNAL_SHELL', kIsWindows)
+if not use_lit_shell:
+    config.available_features.add('shell')
+
 config.test_format = swift_test.SwiftTest(coverage_mode=config.coverage_mode,
-                                          execute_external=use_lit_shell)
+                                          execute_external=not use_lit_shell)
 
 # suffixes: A list of file extensions to treat as test files.
 config.suffixes = ['.swift', '.ll', '.sil', '.gyb', '.m', '.swiftinterface',