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',