Merge pull request #16832 from gottesmm/pr-769c02db89f9caceec9c48e9e9f081fe78655b9a

[func-sig-opts][+0->+1] Add a benchmark that requires the optimizer t…
diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt
index a080adf..6774f7c 100644
--- a/benchmark/CMakeLists.txt
+++ b/benchmark/CMakeLists.txt
@@ -45,6 +45,7 @@
     single-source/BitCount
     single-source/ByteSwap
     single-source/COWTree
+    single-source/COWArrayGuaranteedParameterOverhead
     single-source/CString
     single-source/CSVParsing
     single-source/Calculator
diff --git a/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake
index e5764b7..ee9286b 100644
--- a/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake
+++ b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake
@@ -2,24 +2,6 @@
 include(CMakeParseArguments)
 include(SwiftBenchmarkUtils)
 
-# Run a shell command and assign output to a variable or fail with an error.
-# Example usage:
-#   runcmd(COMMAND "xcode-select" "-p"
-#          VARIABLE xcodepath
-#          ERROR "Unable to find current Xcode path")
-function(runcmd)
-  cmake_parse_arguments(RUNCMD "" "VARIABLE;ERROR" "COMMAND" ${ARGN})
-  execute_process(
-      COMMAND ${RUNCMD_COMMAND}
-      OUTPUT_VARIABLE ${RUNCMD_VARIABLE}
-      RESULT_VARIABLE result
-      ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
-  if(NOT "${result}" MATCHES "0")
-    message(FATAL_ERROR "${RUNCMD_ERROR}")
-  endif()
-  set(${RUNCMD_VARIABLE} ${${RUNCMD_VARIABLE}} PARENT_SCOPE)
-endfunction(runcmd)
-
 function (add_swift_benchmark_library objfile_out sibfile_out)
   cmake_parse_arguments(BENCHLIB "" "MODULE_PATH;SOURCE_DIR;OBJECT_DIR" "SOURCES;LIBRARY_FLAGS;DEPENDS" ${ARGN})
 
diff --git a/benchmark/cmake/modules/SwiftBenchmarkUtils.cmake b/benchmark/cmake/modules/SwiftBenchmarkUtils.cmake
index 9c5e6a0..d457bac 100644
--- a/benchmark/cmake/modules/SwiftBenchmarkUtils.cmake
+++ b/benchmark/cmake/modules/SwiftBenchmarkUtils.cmake
@@ -43,3 +43,22 @@
     set("${var_name}" "" PARENT_SCOPE)
   endif()
 endfunction()
+
+
+# Run a shell command and assign output to a variable or fail with an error.
+# Example usage:
+#   runcmd(COMMAND "xcode-select" "-p"
+#          VARIABLE xcodepath
+#          ERROR "Unable to find current Xcode path")
+function(runcmd)
+  cmake_parse_arguments(RUNCMD "" "VARIABLE;ERROR" "COMMAND" ${ARGN})
+  execute_process(
+      COMMAND ${RUNCMD_COMMAND}
+      OUTPUT_VARIABLE ${RUNCMD_VARIABLE}
+      RESULT_VARIABLE result
+      ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
+  if(NOT "${result}" MATCHES "0")
+    message(FATAL_ERROR "${RUNCMD_ERROR}")
+  endif()
+  set(${RUNCMD_VARIABLE} ${${RUNCMD_VARIABLE}} PARENT_SCOPE)
+endfunction(runcmd)
diff --git a/benchmark/single-source/COWArrayGuaranteedParameterOverhead.swift b/benchmark/single-source/COWArrayGuaranteedParameterOverhead.swift
new file mode 100644
index 0000000..568c994
--- /dev/null
+++ b/benchmark/single-source/COWArrayGuaranteedParameterOverhead.swift
@@ -0,0 +1,42 @@
+//===--- COWArrayGuaranteedParameterOverhead.swift ------------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2018 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
+//
+//===----------------------------------------------------------------------===//
+
+import TestsUtils
+
+// This test makes sure that even though we have +0 arguments by default that we
+// can properly convert +0 -> +1 to avoid extra COW copies.
+
+public let COWArrayGuaranteedParameterOverhead = BenchmarkInfo(
+  name: "COWArrayGuaranteedParameterOverhead",
+  runFunction: run_COWArrayGuaranteedParameterOverhead,
+  tags: [.regression, .abstraction, .refcount])
+
+@inline(never)
+func caller() {
+  let x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+  blackHole(callee(x))
+}
+
+@inline(never)
+func callee(_ x: [Int]) -> [Int] {
+  var y = x
+  // This should not copy.
+  y[2] = 27
+  return y
+}
+
+@inline(never)
+public func run_COWArrayGuaranteedParameterOverhead(_ N: Int) {
+  for _ in 0..<N*20000 {
+    caller()
+  }
+}
diff --git a/benchmark/utils/main.swift b/benchmark/utils/main.swift
index 90c5c4c..7f82ac9 100644
--- a/benchmark/utils/main.swift
+++ b/benchmark/utils/main.swift
@@ -32,6 +32,7 @@
 import BitCount
 import ByteSwap
 import COWTree
+import COWArrayGuaranteedParameterOverhead
 import CString
 import CSVParsing
 import Calculator
@@ -185,6 +186,7 @@
 registerBenchmark(BitCount)
 registerBenchmark(ByteSwap)
 registerBenchmark(COWTree)
+registerBenchmark(COWArrayGuaranteedParameterOverhead)
 registerBenchmark(CString)
 registerBenchmark(CSVParsing)
 registerBenchmark(CSVParsingAlt)
diff --git a/lib/SIL/DynamicCasts.cpp b/lib/SIL/DynamicCasts.cpp
index 56c6c6c..1604f8d 100644
--- a/lib/SIL/DynamicCasts.cpp
+++ b/lib/SIL/DynamicCasts.cpp
@@ -323,10 +323,15 @@
 
   // Casting to a less-optional type can always fail.
   } else if (sourceObject) {
-    return atBest(classifyDynamicCast(M, sourceObject, target,
-                                      /* isSourceTypeExact */ false,
-                                      isWholeModuleOpts),
-                  DynamicCastFeasibility::MaySucceed);
+    auto result = atBest(classifyDynamicCast(M, sourceObject, target,
+                                             /* isSourceTypeExact */ false,
+                                             isWholeModuleOpts),
+                         DynamicCastFeasibility::MaySucceed);
+    if (target.isExistentialType()) {
+      result = atWorst(result, classifyDynamicCastToProtocol(
+                                   M, source, target, isWholeModuleOpts));
+    }
+    return result;
   }
   assert(!sourceObject && !targetObject);
 
diff --git a/test/SILOptimizer/cast_folding.swift b/test/SILOptimizer/cast_folding.swift
index 26a71b9..6f27f50 100644
--- a/test/SILOptimizer/cast_folding.swift
+++ b/test/SILOptimizer/cast_folding.swift
@@ -1032,6 +1032,39 @@
   return P.Type.self as? P.Type.Type
 }
 
+protocol PForOptional {
+}
+
+extension Optional: PForOptional {
+}
+
+func testCastToPForOptional<T>(_ t: T) -> Bool {
+  if let _ = t as? PForOptional {
+    return true
+  }
+  return false
+}
+
+// CHECK-LABEL: // testCastToPForOptionalSuccess()
+// CHECK: [[RES:%.*]] = integer_literal $Builtin.Int1, -1
+// CHECK: [[RET:%.*]] = struct $Bool ([[RES]] : $Builtin.Int1)
+// CHECK: return [[RET]] : $Bool
+@inline(never)
+public func testCastToPForOptionalSuccess() -> Bool {
+  let t: Int? = 42
+  return testCastToPForOptional(t)
+}
+
+// CHECK-LABEL: // testCastToPForOptionalFailure()
+// CHECK: [[RES:%.*]] = integer_literal $Builtin.Int1, 0
+// CHECK: [[RET:%.*]] = struct $Bool ([[RES]] : $Builtin.Int1)
+// CHECK: return [[RET]] : $Bool
+@inline(never)
+public func testCastToPForOptionalFailure() -> Bool {
+  let t: Int = 42
+  return testCastToPForOptional(t)
+}
+
 print("test0=\(test0())")
 
 print("test1=\(test1())")